From 3831bd994110da28088646e3e4c730c2f371f1ab Mon Sep 17 00:00:00 2001 From: Alexander Rose <alexander.rose@weirdbyte.de> Date: Sat, 6 Mar 2021 11:17:43 -0800 Subject: [PATCH] improve handling of coarse grained models --- src/mol-model-props/computed/interactions.ts | 2 +- .../computed/representations/interactions.ts | 2 +- src/mol-model/structure/model/model.ts | 36 ++++++++++++++++++- .../structure/structure/structure.ts | 12 +++---- .../structure/unit/bonds/inter-compute.ts | 2 +- .../structure/unit/bonds/intra-compute.ts | 3 +- .../structure/representation-preset.ts | 23 ++++++++---- .../structure-focus-representation.ts | 4 +-- .../structure/representation/spacefill.ts | 6 +++- .../structure/visual/element-point.ts | 3 +- 10 files changed, 69 insertions(+), 24 deletions(-) diff --git a/src/mol-model-props/computed/interactions.ts b/src/mol-model-props/computed/interactions.ts index e299e9f96..1388fca26 100644 --- a/src/mol-model-props/computed/interactions.ts +++ b/src/mol-model-props/computed/interactions.ts @@ -28,7 +28,7 @@ export const InteractionsProvider: CustomStructureProperty.Provider<Interactions type: 'local', defaultParams: InteractionsParams, getParams: (data: Structure) => InteractionsParams, - isApplicable: (data: Structure) => true, + isApplicable: (data: Structure) => !data.isCoarseGrained, obtain: async (ctx: CustomProperty.Context, data: Structure, props: Partial<InteractionsProps>) => { const p = { ...PD.getDefaultValues(InteractionsParams), ...props }; return { value: await computeInteractions(ctx, data, p) }; diff --git a/src/mol-model-props/computed/representations/interactions.ts b/src/mol-model-props/computed/representations/interactions.ts index d8dcc7477..9e671ecbe 100644 --- a/src/mol-model-props/computed/representations/interactions.ts +++ b/src/mol-model-props/computed/representations/interactions.ts @@ -46,7 +46,7 @@ export const InteractionsRepresentationProvider = StructureRepresentationProvide defaultValues: PD.getDefaultValues(InteractionsParams), defaultColorTheme: { name: 'interaction-type' }, defaultSizeTheme: { name: 'uniform' }, - isApplicable: (structure: Structure) => structure.elementCount > 0, + isApplicable: (structure: Structure) => structure.elementCount > 0 && InteractionsProvider.isApplicable(structure), ensureCustomProperties: { attach: (ctx: CustomProperty.Context, structure: Structure) => InteractionsProvider.attach(ctx, structure, void 0, true), detach: (data) => InteractionsProvider.ref(data, false) diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts index 9591c0035..0ddf806eb 100644 --- a/src/mol-model/structure/model/model.ts +++ b/src/mol-model/structure/model/model.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017-2021 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> @@ -29,6 +29,7 @@ import { CustomModelProperty } from '../../../mol-model-props/common/custom-mode import { Trajectory, ArrayTrajectory } from '../trajectory'; import { Unit } from '../structure'; import { SortedArray } from '../../../mol-data/int/sorted-array'; +import { PolymerType } from './types'; /** * Interface to the "source data" of the molecule. @@ -224,6 +225,39 @@ export namespace Model { } }; + const CoarseGrainedProp = '__CoarseGrained__'; + /** + * Has typical coarse grained atom names (BB, SC1) or less than twice as many + * atoms as polymer residues (C-alpha only models). + */ + export function isCoarseGrained(model: Model): boolean { + if (model._staticPropertyData[CoarseGrainedProp] !== undefined) return model._staticPropertyData[CoarseGrainedProp]; + + let polymerResidueCount = 0; + const { polymerType } = model.atomicHierarchy.derived.residue; + for (let i = 0; i < polymerType.length; ++i) { + if (polymerType[i] !== PolymerType.NA) polymerResidueCount += 1; + } + + // check for coarse grained atom names + let hasBB = false, hasSC1 = false; + const { label_atom_id, _rowCount: atomCount } = model.atomicHierarchy.atoms; + for (let i = 0; i < atomCount; ++i) { + const atomName = label_atom_id.value(i); + if (!hasBB && atomName === 'BB') hasBB = true; + if (!hasSC1 && atomName === 'SC1') hasSC1 = true; + if (hasBB && hasSC1) break; + } + + const coarseGrained = (hasBB && hasSC1) || ( + polymerResidueCount && atomCount + ? atomCount / polymerResidueCount < 2 + : false + ); + model._staticPropertyData[CoarseGrainedProp] = coarseGrained; + return coarseGrained; + } + // export function hasCarbohydrate(model: Model): boolean { diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts index 918bb4869..d39399881 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -158,13 +158,11 @@ class Structure { } /** - * Coarse-grained structure, defined as containing less than - * twice as many elements as polymer residues + * True if any model the structure is based on is coarse grained. + * @see Model.isCoarseGrained */ get isCoarseGrained() { - const ec = this.elementCount; - const prc = this.polymerResidueCount; - return prc && ec ? ec / prc < 2 : false; + return this.models.some(m => Model.isCoarseGrained(m)); } get isEmpty() { @@ -299,7 +297,7 @@ class Structure { /** Contains only atomic units */ get isAtomic() { - for (const u of this.units) if (Unit.isAtomic(u)) return false; + for (const u of this.units) if (!Unit.isAtomic(u)) return false; return true; } @@ -311,7 +309,7 @@ class Structure { /** Contains only coarse units */ get isCoarse() { - for (const u of this.units) if (Unit.isCoarse(u)) return false; + for (const u of this.units) if (!Unit.isCoarse(u)) return false; return true; } 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 e0bc479ae..1a30fcce4 100644 --- a/src/mol-model/structure/structure/unit/bonds/inter-compute.ts +++ b/src/mol-model/structure/structure/unit/bonds/inter-compute.ts @@ -176,7 +176,7 @@ export interface InterBondComputationProps extends BondComputationProps { function findBonds(structure: Structure, props: InterBondComputationProps) { const builder = new InterUnitGraph.Builder<number, StructureElement.UnitIndex, InterUnitEdgeProps>(); - if (props.noCompute) { + if (props.noCompute || structure.isCoarseGrained) { // TODO add function that only adds bonds defined in structConn and avoids using // structure.lookup and unit.lookup (expensive for large structure and not // needed for archival files or files with an MD topology) 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 03ff72f63..81f20fbcb 100644 --- a/src/mol-model/structure/structure/unit/bonds/intra-compute.ts +++ b/src/mol-model/structure/structure/unit/bonds/intra-compute.ts @@ -19,6 +19,7 @@ import { StructConn } from '../../../../../mol-model-formats/structure/property/ import { Vec3 } from '../../../../../mol-math/linear-algebra'; import { ElementIndex } from '../../../model/indexing'; import { equalEps } from '../../../../../mol-math/linear-algebra/3d/common'; +import { Model } from '../../../model/model'; function getGraph(atomA: StructureElement.UnitIndex[], atomB: StructureElement.UnitIndex[], _order: number[], _flags: number[], atomCount: number, canRemap: boolean): IntraUnitBonds { const builder = new IntAdjacencyGraph.EdgeBuilder(atomCount, atomA, atomB); @@ -233,7 +234,7 @@ function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBon function computeIntraUnitBonds(unit: Unit.Atomic, props?: Partial<BondComputationProps>) { const p = { ...DefaultBondComputationProps, ...props }; - if (p.noCompute) { + if (p.noCompute || Model.isCoarseGrained(unit.model)) { // TODO add function that only adds bonds defined in structConn of chemCompBond // and avoid using unit.lookup return IntraUnitBonds.Empty; diff --git a/src/mol-plugin-state/builder/structure/representation-preset.ts b/src/mol-plugin-state/builder/structure/representation-preset.ts index f218f9a0f..559b34f59 100644 --- a/src/mol-plugin-state/builder/structure/representation-preset.ts +++ b/src/mol-plugin-state/builder/structure/representation-preset.ts @@ -22,6 +22,7 @@ import { ChainIdColorThemeProvider } from '../../../mol-theme/color/chain-id'; import { OperatorNameColorThemeProvider } from '../../../mol-theme/color/operator-name'; import { IndexPairBonds } from '../../../mol-model-formats/structure/property/bonds/index-pair'; import { StructConn } from '../../../mol-model-formats/structure/property/bonds/struct_conn'; +import { StructureRepresentationRegistry } from '../../../mol-repr/structure/registry'; export interface StructureRepresentationPresetProvider<P = any, S extends _Result = _Result> extends PresetProvider<PluginStateObject.Molecule.Structure, P, S> { } export function StructureRepresentationPresetProvider<P, S extends _Result>(repr: StructureRepresentationPresetProvider<P, S>) { return repr; } @@ -230,7 +231,7 @@ const coarseSurface = StructureRepresentationPresetProvider({ id: 'preset-structure-representation-coarse-surface', display: { name: 'Coarse Surface', group: BuiltInPresetGroupName, - description: 'Shows polymers as coarse Gaussian Surface.' + description: 'Shows polymers and lipids as coarse Gaussian Surface.' }, params: () => CommonParams, async apply(ref, params, plugin) { @@ -238,7 +239,8 @@ const coarseSurface = StructureRepresentationPresetProvider({ if (!structureCell) return {}; const components = { - polymer: await presetStaticComponent(plugin, structureCell, 'polymer') + polymer: await presetStaticComponent(plugin, structureCell, 'polymer'), + lipid: await presetStaticComponent(plugin, structureCell, 'lipid'), }; const structure = structureCell.obj!.data; @@ -246,7 +248,7 @@ const coarseSurface = StructureRepresentationPresetProvider({ const gaussianProps = Object.create(null); if (size === Structure.Size.Gigantic) { Object.assign(gaussianProps, { - traceOnly: true, + traceOnly: !structure.isCoarseGrained, radiusOffset: 2, smoothness: 1, visuals: ['structure-gaussian-surface-mesh'] @@ -266,7 +268,8 @@ const coarseSurface = StructureRepresentationPresetProvider({ const { update, builder, typeParams, symmetryColor } = reprBuilder(plugin, params, structure); const representations = { - polymer: builder.buildRepresentation(update, components.polymer, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor }, { tag: 'polymer' }) + polymer: builder.buildRepresentation(update, components.polymer, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor }, { tag: 'polymer' }), + lipid: builder.buildRepresentation(update, components.lipid, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor }, { tag: 'lipid' }) }; await update.commit({ revertOnError: true }); @@ -337,9 +340,15 @@ const atomicDetail = StructureRepresentationPresetProvider({ const m = structure.models[0]; const bondsGiven = !!IndexPairBonds.Provider.get(m) || StructConn.isExhaustive(m); - const atomicType = lowResidueElementRatio && !bondsGiven - ? 'spacefill' : highElementCount - ? 'line' : 'ball-and-stick'; + let atomicType: StructureRepresentationRegistry.BuiltIn = 'ball-and-stick'; + if (structure.isCoarseGrained) { + // TODO make configurable? + atomicType = structure.elementCount > 1_000_000 ? 'point' : 'spacefill'; + } else if (lowResidueElementRatio && !bondsGiven) { + atomicType = 'spacefill'; + } else if (highElementCount) { + atomicType = 'line'; + } const showCarbohydrateSymbol = params.showCarbohydrateSymbol && !highElementCount && !lowResidueElementRatio; if (showCarbohydrateSymbol) { diff --git a/src/mol-plugin/behavior/dynamic/selection/structure-focus-representation.ts b/src/mol-plugin/behavior/dynamic/selection/structure-focus-representation.ts index ea87f7a3e..ad7452f0d 100644 --- a/src/mol-plugin/behavior/dynamic/selection/structure-focus-representation.ts +++ b/src/mol-plugin/behavior/dynamic/selection/structure-focus-representation.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2019-2021 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> @@ -107,7 +107,7 @@ class StructureFocusRepresentationBehavior extends PluginBehavior.WithSubscriber .apply(StateTransforms.Representation.StructureRepresentation3D, this.getReprParams(this.params.surroundingsParams), { tags: StructureFocusRepresentationTags.SurrRepr }).ref; } - if (components.indexOf('interactions') >= 0 && !refs[StructureFocusRepresentationTags.SurrNciRepr]) { + if (components.indexOf('interactions') >= 0 && !refs[StructureFocusRepresentationTags.SurrNciRepr] && cell.obj && InteractionsRepresentationProvider.isApplicable(cell.obj?.data)) { refs[StructureFocusRepresentationTags.SurrNciRepr] = builder .to(refs[StructureFocusRepresentationTags.SurrSel]!) .apply(StateTransforms.Representation.StructureRepresentation3D, this.params.nciParams, { tags: StructureFocusRepresentationTags.SurrNciRepr }).ref; diff --git a/src/mol-repr/structure/representation/spacefill.ts b/src/mol-repr/structure/representation/spacefill.ts index d50d33fa6..ac6aeeef2 100644 --- a/src/mol-repr/structure/representation/spacefill.ts +++ b/src/mol-repr/structure/representation/spacefill.ts @@ -21,7 +21,11 @@ export const SpacefillParams = { }; export type SpacefillParams = typeof SpacefillParams export function getSpacefillParams(ctx: ThemeRegistryContext, structure: Structure) { - return PD.clone(SpacefillParams); + const params = PD.clone(SpacefillParams); + if (structure.isCoarseGrained) { + params.sizeFactor.defaultValue = 2; + } + return params; } export type SpacefillRepresentation = StructureRepresentation<SpacefillParams> diff --git a/src/mol-repr/structure/visual/element-point.ts b/src/mol-repr/structure/visual/element-point.ts index 8d90bd8e9..296276588 100644 --- a/src/mol-repr/structure/visual/element-point.ts +++ b/src/mol-repr/structure/visual/element-point.ts @@ -18,8 +18,7 @@ import { Sphere3D } from '../../../mol-math/geometry'; export const ElementPointParams = { ...UnitsPointsParams, - // sizeFactor: PD.Numeric(1.0, { min: 0, max: 10, step: 0.01 }), - pointSizeAttenuation: PD.Boolean(false), + pointSizeAttenuation: PD.Boolean(true), ignoreHydrogens: PD.Boolean(false), traceOnly: PD.Boolean(false), }; -- GitLab