|
@@ -1,5 +1,5 @@
|
|
|
/**
|
|
|
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
|
|
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
|
|
*
|
|
|
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
|
|
*/
|
|
@@ -12,8 +12,6 @@ import { PluginContext } from '../context';
|
|
|
import { StructureRepresentation3DHelpers } from '../state/transforms/representation';
|
|
|
import Expression from '../../mol-script/language/expression';
|
|
|
import { compile } from '../../mol-script/runtime/query/compiler';
|
|
|
-import { StructureSelectionQueries as Q } from '../util/structure-selection-helper';
|
|
|
-import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
|
|
import { VisualQuality } from '../../mol-geo/geometry/base';
|
|
|
|
|
|
type StructureTransform = StateObjectCell<PSO.Molecule.Structure, StateTransform<StateTransformer<any, PSO.Molecule.Structure, any>>>
|
|
@@ -34,6 +32,12 @@ function getCombinedLoci(mode: SelectionModifier, loci: StructureElement.Loci, c
|
|
|
|
|
|
type SelectionModifier = 'add' | 'remove' | 'only'
|
|
|
|
|
|
+type ReprProps = {
|
|
|
+ repr?: {},
|
|
|
+ color?: string | [string, {}],
|
|
|
+ size?: string | [string, {}],
|
|
|
+}
|
|
|
+
|
|
|
export class StructureRepresentationHelper {
|
|
|
getRepresentationStructure(rootRef: string, type: string) {
|
|
|
const state = this.plugin.state.dataState
|
|
@@ -49,12 +53,52 @@ export class StructureRepresentationHelper {
|
|
|
return selections.length > 0 ? selections[0] : undefined
|
|
|
}
|
|
|
|
|
|
- private async _set(modifier: SelectionModifier, type: string, loci: StructureElement.Loci, structure: StructureTransform, props = {}) {
|
|
|
+ private getRepresentationParams(structure: Structure, type: string, repr: RepresentationTransform | undefined, props: ReprProps = {}) {
|
|
|
+ const reprProps = {
|
|
|
+ ...(repr?.params && repr.params.values.type.params),
|
|
|
+ ignoreHydrogens: this._ignoreHydrogens,
|
|
|
+ quality: this._quality,
|
|
|
+ ...props.repr
|
|
|
+ }
|
|
|
+ const { themeCtx } = this.plugin.structureRepresentation
|
|
|
+
|
|
|
+ const p: StructureRepresentation3DHelpers.Props = {
|
|
|
+ repr: [
|
|
|
+ this.plugin.structureRepresentation.registry.get(type),
|
|
|
+ () => reprProps
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ if (props.color) {
|
|
|
+ const colorType = props.color instanceof Array ? props.color[0] : props.color
|
|
|
+ const colorTheme = themeCtx.colorThemeRegistry.get(colorType)
|
|
|
+ const colorProps = {
|
|
|
+ ...(repr?.params && repr.params.values.colorTheme.params),
|
|
|
+ ...(props.color instanceof Array ? props.color[1] : {})
|
|
|
+ }
|
|
|
+ p.color = [colorTheme, () => colorProps]
|
|
|
+ }
|
|
|
+ if (props.size) {
|
|
|
+ const sizeType = props.size instanceof Array ? props.size[0] : props.size
|
|
|
+ const sizeTheme = themeCtx.sizeThemeRegistry.get(sizeType)
|
|
|
+ const sizeProps = {
|
|
|
+ ...(repr?.params && repr.params.values.sizeTheme.params),
|
|
|
+ ...(props.size instanceof Array ? props.size[1] : {})
|
|
|
+ }
|
|
|
+ p.size = [sizeTheme, () => sizeProps]
|
|
|
+ }
|
|
|
+ if (props.size) p.size = props.size
|
|
|
+
|
|
|
+ return StructureRepresentation3DHelpers.createParams(this.plugin, structure, p)
|
|
|
+ }
|
|
|
+
|
|
|
+ private async _set(modifier: SelectionModifier, type: string, loci: StructureElement.Loci, structure: StructureTransform, props: ReprProps = {}) {
|
|
|
const state = this.plugin.state.dataState
|
|
|
const update = state.build()
|
|
|
const s = structure.obj!.data
|
|
|
|
|
|
+ const repr = this.getRepresentation(structure.transform.ref, type)
|
|
|
const reprStructure = this.getRepresentationStructure(structure.transform.ref, type)
|
|
|
+ const reprParams = this.getRepresentationParams(s, type, repr, props)
|
|
|
|
|
|
if (reprStructure) {
|
|
|
const currentLoci = StructureElement.Bundle.toLoci(reprStructure.params!.values.bundle, s)
|
|
@@ -64,14 +108,9 @@ export class StructureRepresentationHelper {
|
|
|
...reprStructure.params!.values,
|
|
|
bundle: StructureElement.Bundle.fromLoci(combinedLoci)
|
|
|
})
|
|
|
+ if (repr) update.to(repr).update(reprParams)
|
|
|
} else {
|
|
|
const combinedLoci = getCombinedLoci(modifier, loci, StructureElement.Loci.none(s))
|
|
|
- const params = StructureRepresentation3DHelpers.getDefaultParams(this.plugin, type as any, s)
|
|
|
-
|
|
|
- const p = params.type.params
|
|
|
- Object.assign(p, props)
|
|
|
- if (p.ignoreHydrogens !== undefined) p.ignoreHydrogens = this._ignoreHydrogens
|
|
|
- if (p.quality !== undefined) p.quality = this._quality
|
|
|
|
|
|
update.to(structure.transform.ref)
|
|
|
.apply(
|
|
@@ -79,13 +118,13 @@ export class StructureRepresentationHelper {
|
|
|
{ bundle: StructureElement.Bundle.fromLoci(combinedLoci), label: type },
|
|
|
{ tags: [ RepresentationManagerTag, getRepresentationManagerTag(type) ] }
|
|
|
)
|
|
|
- .apply( StateTransforms.Representation.StructureRepresentation3D, params)
|
|
|
+ .apply(StateTransforms.Representation.StructureRepresentation3D, reprParams)
|
|
|
}
|
|
|
|
|
|
await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true }))
|
|
|
}
|
|
|
|
|
|
- async set(modifier: SelectionModifier, type: string, lociGetter: (structure: Structure) => StructureElement.Loci, props = {}) {
|
|
|
+ async set(modifier: SelectionModifier, type: string, lociGetter: (structure: Structure) => StructureElement.Loci, props: ReprProps = {}) {
|
|
|
const state = this.plugin.state.dataState;
|
|
|
const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure))
|
|
|
|
|
@@ -96,7 +135,7 @@ export class StructureRepresentationHelper {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- async setFromExpression(modifier: SelectionModifier, type: string, expression: Expression, props = {}) {
|
|
|
+ async setFromExpression(modifier: SelectionModifier, type: string, expression: Expression, props: ReprProps = {}) {
|
|
|
return this.set(modifier, type, (structure) => {
|
|
|
const compiled = compile<StructureSelection>(expression)
|
|
|
const result = compiled(new QueryContext(structure))
|
|
@@ -104,44 +143,76 @@ export class StructureRepresentationHelper {
|
|
|
}, props)
|
|
|
}
|
|
|
|
|
|
- async clear() {
|
|
|
+ async eachStructure(callback: (structure: StructureTransform, type: string, update: StateBuilder.Root) => void) {
|
|
|
const { registry } = this.plugin.structureRepresentation
|
|
|
const state = this.plugin.state.dataState;
|
|
|
const update = state.build()
|
|
|
const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure))
|
|
|
- const bundle = StructureElement.Bundle.Empty
|
|
|
|
|
|
for (const structure of structures) {
|
|
|
for (let i = 0, il = registry.types.length; i < il; ++i) {
|
|
|
const type = registry.types[i][0]
|
|
|
const reprStructure = this.getRepresentationStructure(structure.transform.ref, type)
|
|
|
- if (reprStructure) {
|
|
|
- update.to(reprStructure).update({ ...reprStructure.params!.values, bundle })
|
|
|
- }
|
|
|
+ if (reprStructure) callback(reprStructure, type, update)
|
|
|
}
|
|
|
}
|
|
|
await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true }))
|
|
|
}
|
|
|
|
|
|
- async eachRepresentation(callback: (repr: RepresentationTransform, update: StateBuilder.Root) => void) {
|
|
|
+ async clear() {
|
|
|
+ const bundle = StructureElement.Bundle.Empty
|
|
|
+ await this.eachStructure((structure, type, update) => {
|
|
|
+ update.to(structure).update({ ...structure.params!.values, bundle })
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ async clearExcept(exceptTypes: string[]) {
|
|
|
+ const bundle = StructureElement.Bundle.Empty
|
|
|
+ await this.eachStructure((structure, type, update) => {
|
|
|
+ if (!exceptTypes.includes(type)) {
|
|
|
+ update.to(structure).update({ ...structure.params!.values, bundle })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ async eachRepresentation(callback: (repr: RepresentationTransform, type: string, update: StateBuilder.Root) => void) {
|
|
|
const { registry } = this.plugin.structureRepresentation
|
|
|
const state = this.plugin.state.dataState;
|
|
|
const update = state.build()
|
|
|
const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure))
|
|
|
for (const structure of structures) {
|
|
|
for (let i = 0, il = registry.types.length; i < il; ++i) {
|
|
|
- const repr = this.getRepresentation(structure.transform.ref, registry.types[i][0])
|
|
|
- if (repr) callback(repr, update)
|
|
|
+ const type = registry.types[i][0]
|
|
|
+ const repr = this.getRepresentation(structure.transform.ref, type)
|
|
|
+ if (repr) callback(repr, type, update)
|
|
|
}
|
|
|
}
|
|
|
await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true }))
|
|
|
}
|
|
|
|
|
|
+ setRepresentationParams(repr: RepresentationTransform, type: string, update: StateBuilder.Root, props: ReprProps) {
|
|
|
+ const state = this.plugin.state.dataState;
|
|
|
+ const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure))
|
|
|
+
|
|
|
+ for (const structure of structures) {
|
|
|
+ const s = structure.obj!.data
|
|
|
+ const reprParams = this.getRepresentationParams(s, type, repr, props)
|
|
|
+ update.to(repr).update(reprParams)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async updateRepresentation(repr: RepresentationTransform, type: string, props: ReprProps) {
|
|
|
+ const state = this.plugin.state.dataState;
|
|
|
+ const update = state.build()
|
|
|
+ this.setRepresentationParams(repr, type, update, props)
|
|
|
+ await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true }))
|
|
|
+ }
|
|
|
+
|
|
|
private _ignoreHydrogens = false
|
|
|
get ignoreHydrogens () { return this._ignoreHydrogens }
|
|
|
async setIgnoreHydrogens(ignoreHydrogens: boolean) {
|
|
|
if (ignoreHydrogens === this._ignoreHydrogens) return
|
|
|
- await this.eachRepresentation((repr, update) => {
|
|
|
+ await this.eachRepresentation((repr, type, update) => {
|
|
|
if (repr.params && repr.params.values.type.params.ignoreHydrogens !== undefined) {
|
|
|
const { name, params } = repr.params.values.type
|
|
|
update.to(repr.transform.ref).update(
|
|
@@ -157,7 +228,7 @@ export class StructureRepresentationHelper {
|
|
|
get quality () { return this._quality }
|
|
|
async setQuality(quality: VisualQuality) {
|
|
|
if (quality === this._quality) return
|
|
|
- await this.eachRepresentation((repr, update) => {
|
|
|
+ await this.eachRepresentation((repr, type, update) => {
|
|
|
if (repr.params && repr.params.values.type.params.quality !== undefined) {
|
|
|
const { name, params } = repr.params.values.type
|
|
|
update.to(repr.transform.ref).update(
|
|
@@ -169,74 +240,7 @@ export class StructureRepresentationHelper {
|
|
|
this._quality = quality
|
|
|
}
|
|
|
|
|
|
- async preset() {
|
|
|
- // TODO option to limit to specific structure
|
|
|
- const state = this.plugin.state.dataState;
|
|
|
- const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure))
|
|
|
-
|
|
|
- if (structures.length === 0) return
|
|
|
- const s = structures[0].obj!.data
|
|
|
-
|
|
|
- if (s.elementCount < 50000) {
|
|
|
- await polymerAndLigand(this)
|
|
|
- } else if (s.elementCount < 200000) {
|
|
|
- await proteinAndNucleic(this)
|
|
|
- } else {
|
|
|
- if (s.unitSymmetryGroups[0].units.length > 10) {
|
|
|
- await capsid(this)
|
|
|
- } else {
|
|
|
- await coarseCapsid(this)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
constructor(private plugin: PluginContext) {
|
|
|
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-//
|
|
|
-
|
|
|
-async function polymerAndLigand(r: StructureRepresentationHelper) {
|
|
|
- await r.clear()
|
|
|
- await r.setFromExpression('add', 'cartoon', Q.polymer.expression)
|
|
|
- await r.setFromExpression('add', 'carbohydrate', Q.branchedPlusConnected.expression)
|
|
|
- await r.setFromExpression('add', 'ball-and-stick', MS.struct.modifier.union([
|
|
|
- MS.struct.combinator.merge([
|
|
|
- Q.ligandPlusConnected.expression,
|
|
|
- Q.branchedConnectedOnly.expression,
|
|
|
- Q.disulfideBridges.expression,
|
|
|
- Q.nonStandardPolymer.expression,
|
|
|
- Q.water.expression
|
|
|
- ])
|
|
|
- ]))
|
|
|
-}
|
|
|
-
|
|
|
-async function proteinAndNucleic(r: StructureRepresentationHelper) {
|
|
|
- await r.clear()
|
|
|
- await r.setFromExpression('add', 'cartoon', Q.protein.expression)
|
|
|
- await r.setFromExpression('add', 'gaussian-surface', Q.nucleic.expression)
|
|
|
-}
|
|
|
-
|
|
|
-async function capsid(r: StructureRepresentationHelper) {
|
|
|
- await r.clear()
|
|
|
- await r.setFromExpression('add', 'gaussian-surface', Q.polymer.expression, {
|
|
|
- smoothness: 0.5,
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-async function coarseCapsid(r: StructureRepresentationHelper) {
|
|
|
- await r.clear()
|
|
|
- await r.setFromExpression('add', 'gaussian-surface', Q.trace.expression, {
|
|
|
- radiusOffset: 1,
|
|
|
- smoothness: 0.5,
|
|
|
- visuals: ['structure-gaussian-surface-mesh']
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-export const StructureRepresentationPresets = {
|
|
|
- polymerAndLigand,
|
|
|
- proteinAndNucleic,
|
|
|
- capsid,
|
|
|
- coarseCapsid
|
|
|
}
|