From ee776e6e3e011cac738e2bc74053a419ba0c6435 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Fri, 2 Aug 2019 17:18:07 -0700 Subject: [PATCH] wip, structure tools --- .../behavior/dynamic/representation.ts | 2 +- src/mol-plugin/context.ts | 8 +- src/mol-plugin/state/actions/structure.ts | 2 +- src/mol-plugin/state/transforms/model.ts | 11 +- src/mol-plugin/ui/structure/overpaint.tsx | 65 ++--------- .../ui/structure/representation.tsx | 102 ++---------------- src/mol-plugin/ui/structure/selection.tsx | 54 ++-------- src/mol-plugin/ui/structure/util.ts | 17 --- src/mol-plugin/util/interactivity.ts | 2 +- .../util/structure-overpaint-helper.ts | 85 +++++++++++++++ .../util/structure-representation-helper.ts | 102 ++++++++++++++++++ .../util/structure-selection-helper.ts | 55 ++++++++++ 12 files changed, 289 insertions(+), 216 deletions(-) delete mode 100644 src/mol-plugin/ui/structure/util.ts create mode 100644 src/mol-plugin/util/structure-overpaint-helper.ts create mode 100644 src/mol-plugin/util/structure-representation-helper.ts create mode 100644 src/mol-plugin/util/structure-selection-helper.ts diff --git a/src/mol-plugin/behavior/dynamic/representation.ts b/src/mol-plugin/behavior/dynamic/representation.ts index edf502302..09fab1853 100644 --- a/src/mol-plugin/behavior/dynamic/representation.ts +++ b/src/mol-plugin/behavior/dynamic/representation.ts @@ -49,7 +49,7 @@ export const SelectLoci = PluginBehavior.create({ this.spine.current = cell const so = this.spine.getRootOfType(SO.Molecule.Structure) if (so) { - const loci = this.ctx.helpers.structureSelection.get(so.data) + const loci = this.ctx.helpers.structureSelectionManager.get(so.data) this.lociMarkProvider({ loci }, MarkerAction.Select) } } diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts index 024c4046d..e2b6b11a2 100644 --- a/src/mol-plugin/context.ts +++ b/src/mol-plugin/context.ts @@ -37,6 +37,9 @@ import { ModifiersKeys } from '../mol-util/input/input-observer'; import { isProductionMode, isDebugMode } from '../mol-util/debug'; import { Model, Structure } from '../mol-model/structure'; import { Interactivity } from './util/interactivity'; +import { StructureRepresentationHelper } from './util/structure-representation-helper'; +import { StructureSelectionHelper } from './util/structure-selection-helper'; +import { StructureOverpaintHelper } from './util/structure-overpaint-helper'; interface Log { entries: List<LogEntry> @@ -121,7 +124,10 @@ export class PluginContext { readonly customParamEditors = new Map<string, StateTransformParameters.Class>(); readonly helpers = { - structureSelection: new StructureElementSelectionManager(this), + structureSelectionManager: new StructureElementSelectionManager(this), + structureSelection: new StructureSelectionHelper(this), + structureRepresentation: new StructureRepresentationHelper(this), + structureOverpaint: new StructureOverpaintHelper(this), substructureParent: new SubstructureParentHelper(this) } as const; diff --git a/src/mol-plugin/state/actions/structure.ts b/src/mol-plugin/state/actions/structure.ts index 3ecad410b..76914d041 100644 --- a/src/mol-plugin/state/actions/structure.ts +++ b/src/mol-plugin/state/actions/structure.ts @@ -352,7 +352,7 @@ export const StructureFromSelection = StateAction.build({ // return t.transformer !== CustomModelProperties; // } })(({ a, ref, params, state }, plugin: PluginContext) => { - const sel = plugin.helpers.structureSelection.get(a.data); + const sel = plugin.helpers.structureSelectionManager.get(a.data); if (sel.kind === 'empty-loci') return Task.constant('', void 0); const query = StructureElement.Loci.toScriptExpression(sel); diff --git a/src/mol-plugin/state/transforms/model.ts b/src/mol-plugin/state/transforms/model.ts index 0813edd0c..ddc20811a 100644 --- a/src/mol-plugin/state/transforms/model.ts +++ b/src/mol-plugin/state/transforms/model.ts @@ -9,7 +9,7 @@ import { parsePDB } from '../../../mol-io/reader/pdb/parser'; import { Vec3, Mat4, Quat } from '../../../mol-math/linear-algebra'; import { trajectoryFromMmCIF } from '../../../mol-model-formats/structure/mmcif'; import { trajectoryFromPDB } from '../../../mol-model-formats/structure/pdb'; -import { Model, ModelSymmetry, Queries, QueryContext, Structure, StructureQuery, StructureSelection as Sel, StructureSymmetry, QueryFn } from '../../../mol-model/structure'; +import { Model, ModelSymmetry, Queries, QueryContext, Structure, StructureQuery, StructureSelection as Sel, StructureSymmetry, QueryFn, StructureElement } from '../../../mol-model/structure'; import { Assembly } from '../../../mol-model/structure/model/properties/symmetry'; import { PluginContext } from '../../../mol-plugin/context'; import { MolScriptBuilder } from '../../../mol-script/language/builder'; @@ -27,6 +27,7 @@ import { transpileMolScript } from '../../../mol-script/script/mol-script/symbol import { shapeFromPly } from '../../../mol-model-formats/shape/ply'; import { SymmetryOperator } from '../../../mol-math/geometry'; import { ensureSecondaryStructure } from './helpers'; +import { formatMolScript } from '../../../mol-script/language/expression-formatter'; export { TrajectoryFromBlob }; export { TrajectoryFromMmCif }; @@ -385,6 +386,14 @@ const UserStructureSelection = PluginStateTransform.BuiltIn({ (cache as { source: Structure }).source = a.data; const result = compiled(new QueryContext(a.data)); const s = Sel.unionStructure(result); + + // TODO for debug purposes only, later move to StructureSelection where the expression is not visible to the user + let loci = Structure.toStructureElementLoci(Structure.Loci(s)) + if (s.parent) loci = StructureElement.Loci.remap(loci, s.parent) + const expression = formatMolScript(StructureElement.Loci.toScriptExpression(loci)) + console.log({ before: params.query.expression, after: expression }) + params.query.expression = expression + const props = { label: `${params.label || 'Selection'}`, description: structureDesc(s) }; return new SO.Molecule.Structure(s, props); }, diff --git a/src/mol-plugin/ui/structure/overpaint.tsx b/src/mol-plugin/ui/structure/overpaint.tsx index c158f2801..5e1070213 100644 --- a/src/mol-plugin/ui/structure/overpaint.tsx +++ b/src/mol-plugin/ui/structure/overpaint.tsx @@ -6,20 +6,11 @@ import * as React from 'react'; import { PluginUIComponent } from '../base'; -import { PluginStateObject } from '../../../mol-plugin/state/objects'; -import { StateTransforms } from '../../../mol-plugin/state/transforms'; -import { StateSelection, StateObjectCell, StateTransform, StateBuilder } from '../../../mol-state'; import { ParamDefinition as PD} from '../../../mol-util/param-definition'; import { ColorNames } from '../../../mol-util/color/tables'; import { ParameterControls } from '../controls/parameters'; -import { Structure } from '../../../mol-model/structure'; -import { isEmptyLoci } from '../../../mol-model/loci'; import { PluginContext } from '../../context'; -import { getExpression } from './util'; - - -type OverpaintEachReprCallback = (update: StateBuilder.Root, repr: StateObjectCell<PluginStateObject.Molecule.Structure.Representation3D, StateTransform<typeof StateTransforms.Representation.StructureRepresentation3D>>, rootStructure: Structure, overpaint?: StateObjectCell<any, StateTransform<typeof StateTransforms.Representation.OverpaintStructureRepresentation3D>>) => void -const OverpaintManagerTag = 'overpaint-controls' +import { Color } from '../../../mol-util/color'; export class StructureOverpaintControls extends PluginUIComponent<{}, { params: PD.Values<ReturnType<typeof StructureOverpaintControls.getParams>> }> { state = { params: PD.getDefaultValues(StructureOverpaintControls.getParams(this.plugin)) } @@ -36,58 +27,20 @@ export class StructureOverpaintControls extends PluginUIComponent<{}, { params: } - private async eachRepr(callback: OverpaintEachReprCallback) { - const state = this.plugin.state.dataState; - const reprs = state.select(StateSelection.Generators.ofType(PluginStateObject.Molecule.Structure.Representation3D)); - - const update = state.build(); - for (const r of reprs) { - const overpaint = state.select(StateSelection.Generators.ofTransformer(StateTransforms.Representation.OverpaintStructureRepresentation3D, r.transform.ref).withTag(OverpaintManagerTag)); - - const structure = r.obj!.data.source.data - const rootStructure = structure.parent || structure - - callback(update, r, rootStructure, overpaint[0]) - } - - await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true })); - } - - set = async (clear: boolean) => { - await this.eachRepr((update, repr, rootStructure, overpaint) => { - if (!this.state.params.type.includes(repr.params!.values.type.name)) return - - const loci = this.plugin.helpers.structureSelection.get(rootStructure) - if (isEmptyLoci(loci) || loci.elements.length === 0) return - const expression = getExpression(loci) - - const layer = { - script: { language: 'mol-script', expression }, - color: this.state.params.color, - clear - } - - if (overpaint) { - update.to(overpaint).update({ layers: [ ...overpaint.params!.values.layers, layer ], alpha: 1 }) - } else { - update.to(repr.transform.ref) - .apply(StateTransforms.Representation.OverpaintStructureRepresentation3D, { layers: [ layer ], alpha: 1 }, { tags: OverpaintManagerTag }); - } - }) + set = (color: Color | -1) => { + this.plugin.helpers.structureOverpaint.set(color, this.state.params.type) } - add = async () => { - this.set(false) + add = () => { + this.set(this.state.params.color) } - clear = async () => { - this.set(true) + clear = () => { + this.set(-1) } - clearAll = async () => { - await this.eachRepr((update, repr, rootStructure, overpaint) => { - if (overpaint) update.delete(overpaint.transform.ref) - }) + clearAll = () => { + this.plugin.helpers.structureOverpaint.clearAll() } render() { diff --git a/src/mol-plugin/ui/structure/representation.tsx b/src/mol-plugin/ui/structure/representation.tsx index 54e06dfce..5e05374e2 100644 --- a/src/mol-plugin/ui/structure/representation.tsx +++ b/src/mol-plugin/ui/structure/representation.tsx @@ -6,35 +6,9 @@ import * as React from 'react'; import { PluginUIComponent } from '../base'; -import { PluginStateObject } from '../../../mol-plugin/state/objects'; -import { StateTransforms } from '../../../mol-plugin/state/transforms'; -import { StateTransformer, StateSelection, StateObjectCell, StateTransform, StateBuilder } from '../../../mol-state'; import { ParamDefinition as PD} from '../../../mol-util/param-definition'; import { ParameterControls } from '../controls/parameters'; -import { StructureElement, QueryContext, StructureSelection } from '../../../mol-model/structure'; -import { isEmptyLoci } from '../../../mol-model/loci'; import { PluginContext } from '../../context'; -import { getExpression } from './util'; -import { parseMolScript } from '../../../mol-script/language/parser'; -import { transpileMolScript } from '../../../mol-script/script/mol-script/symbols'; -import { compile } from '../../../mol-script/runtime/query/compiler'; -import { StructureRepresentation3DHelpers } from '../../state/transforms/representation'; - -type RepresentationEachStructureCallback = (update: StateBuilder.Root, structure: StateObjectCell<PluginStateObject.Molecule.Structure, StateTransform<StateTransformer<any, PluginStateObject.Molecule.Structure, any>>>) => void -const RepresentationManagerTag = 'representation-controls' - -function getRepresentationManagerTag(type: string) { - return `${RepresentationManagerTag}-${type}` -} - -function getCombinedLoci(mode: 'add' | 'remove' | 'only' | 'all', loci: StructureElement.Loci, currentLoci: StructureElement.Loci): StructureElement.Loci { - switch (mode) { - case 'add': return StructureElement.Loci.union(loci, currentLoci) - case 'remove': return StructureElement.Loci.subtract(currentLoci, loci) - case 'only': return loci - case 'all': return StructureElement.Loci.all(loci.structure) - } -} export class StructureRepresentationControls extends PluginUIComponent<{}, { params: PD.Values<ReturnType<typeof StructureRepresentationControls.getParams>> }> { state = { params: PD.getDefaultValues(StructureRepresentationControls.getParams(this.plugin)) } @@ -50,77 +24,17 @@ export class StructureRepresentationControls extends PluginUIComponent<{}, { par } - private async eachStructure(callback: RepresentationEachStructureCallback) { - const state = this.plugin.state.dataState; - const structures = state.select(StateSelection.Generators.rootsOfType(PluginStateObject.Molecule.Structure)); - - const update = state.build(); - for (const s of structures) { - callback(update, s) - } - - await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true })); - } - - set = async (mode: 'add' | 'remove' | 'only' | 'all') => { - const state = this.plugin.state.dataState - const { type } = this.state.params - - await this.eachStructure((update, structure) => { - const s = structure.obj!.data - const _loci = this.plugin.helpers.structureSelection.get(s) - const loci = isEmptyLoci(_loci) ? StructureElement.Loci(s, []) : _loci - - const selections = state.select(StateSelection.Generators.ofType(PluginStateObject.Molecule.Structure, structure.transform.ref).withTag(getRepresentationManagerTag(type))); - - if (selections.length > 0) { - const parsed = parseMolScript(selections[0].params!.values.query.expression) - if (parsed.length === 0) return - - const query = transpileMolScript(parsed[0]) - const compiled = compile(query) - const result = compiled(new QueryContext(structure.obj!.data)) - const currentLoci = StructureSelection.toLoci2(result) - - const combinedLoci = getCombinedLoci(mode, loci, currentLoci) - - update.to(selections[0]).update({ - ...selections[0].params!.values, - query: { language: 'mol-script', expression: getExpression(combinedLoci) } - }) - } else { - const combinedLoci = getCombinedLoci(mode, loci, StructureElement.Loci(loci.structure, [])) - - update.to(structure.transform.ref) - .apply( - StateTransforms.Model.UserStructureSelection, - { - query: { language: 'mol-script', expression: getExpression(combinedLoci) }, - label: type - }, - { tags: [ RepresentationManagerTag, getRepresentationManagerTag(type) ] } - ) - .apply( - StateTransforms.Representation.StructureRepresentation3D, - StructureRepresentation3DHelpers.getDefaultParams(this.plugin, type as any, s) - ) - } - }) + set = (mode: 'add' | 'remove' | 'only' | 'all') => { + this.plugin.helpers.structureRepresentation.setSelected(mode, this.state.params.type) } - show = async () => { this.set('add') } - hide = async () => { this.set('remove') } - only = async () => { this.set('only') } - showAll = async () => { this.set('all') } - - hideAll = async () => { - const { type } = this.state.params - const state = this.plugin.state.dataState; - const update = state.build(); - - state.select(StateSelection.Generators.ofType(PluginStateObject.Molecule.Structure).withTag(getRepresentationManagerTag(type))).forEach(structure => update.delete(structure.transform.ref)); + show = () => { this.set('add') } + hide = () => { this.set('remove') } + only = () => { this.set('only') } + showAll = () => { this.set('all') } - await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true })); + hideAll = () => { + this.plugin.helpers.structureRepresentation.hideAll(this.state.params.type) } render() { diff --git a/src/mol-plugin/ui/structure/selection.tsx b/src/mol-plugin/ui/structure/selection.tsx index 62b48e5fb..41c23ec88 100644 --- a/src/mol-plugin/ui/structure/selection.tsx +++ b/src/mol-plugin/ui/structure/selection.tsx @@ -6,22 +6,9 @@ import * as React from 'react'; import { PluginUIComponent } from '../base'; -import { MolScriptBuilder as MS } from '../../../mol-script/language/builder'; -import { StateSelection } from '../../../mol-state'; -import { PluginStateObject } from '../../state/objects'; -import { QueryContext, StructureSelection, QueryFn, Queries as _Queries } from '../../../mol-model/structure'; -import { compile } from '../../../mol-script/runtime/query/compiler'; -import { ButtonsType } from '../../../mol-util/input/input-observer'; -import { EmptyLoci } from '../../../mol-model/loci'; +import { StructureSelection, QueryFn, Queries as _Queries } from '../../../mol-model/structure'; import { formatStructureSelectionStats } from '../../util/structure-element-selection'; - -const Queries = { - all: () => compile<StructureSelection>(MS.struct.generator.all()), - polymers: () => _Queries.internal.atomicSequence(), - water: () => _Queries.internal.water(), - ligands: () => _Queries.internal.atomicHet(), - coarse: () => _Queries.internal.spheres(), -} +import { StructureSelectionQueries } from '../../util/structure-selection-helper'; export class StructureSelectionControls extends PluginUIComponent<{}, {}> { state = {} @@ -33,36 +20,15 @@ export class StructureSelectionControls extends PluginUIComponent<{}, {}> { } get stats() { - return formatStructureSelectionStats(this.plugin.helpers.structureSelection.stats) + return formatStructureSelectionStats(this.plugin.helpers.structureSelectionManager.stats) } select = (query: QueryFn<StructureSelection>) => { - const state = this.plugin.state.dataState - const structures = state.select(StateSelection.Generators.rootsOfType(PluginStateObject.Molecule.Structure)) - const { structureSelection } = this.plugin.helpers - - structureSelection.clear() - for (const so of structures) { - const s = so.obj!.data - const result = query(new QueryContext(s)) - const loci = StructureSelection.toLoci2(result) - - // TODO use better API when available - this.plugin.interactivity.lociSelections.apply({ - current: { loci }, - buttons: ButtonsType.Flag.Secondary, - modifiers: { shift: false, alt: false, control: true, meta: false } - }) - } + this.plugin.helpers.structureSelection.select(query) } clear = () => { - // TODO use better API when available - this.plugin.interactivity.lociSelections.apply({ - current: { loci: EmptyLoci }, - buttons: ButtonsType.Flag.Secondary, - modifiers: { shift: false, alt: false, control: true, meta: false } - }) + this.plugin.helpers.structureSelection.clearSelection() } render() { @@ -75,14 +41,14 @@ export class StructureSelectionControls extends PluginUIComponent<{}, {}> { <div>{this.stats}</div> </div> <div className='msp-btn-row-group'> - <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.select(Queries.all())}>All</button> + <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.select(StructureSelectionQueries.all())}>All</button> <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.clear()}>None</button> </div> <div className='msp-btn-row-group'> - <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.select(Queries.polymers())}>Polymers</button> - <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.select(Queries.ligands())}>Ligands</button> - <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.select(Queries.water())}>Water</button> - <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.select(Queries.coarse())}>Coarse</button> + <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.select(StructureSelectionQueries.polymers())}>Polymers</button> + <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.select(StructureSelectionQueries.ligands())}>Ligands</button> + <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.select(StructureSelectionQueries.water())}>Water</button> + <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.select(StructureSelectionQueries.coarse())}>Coarse</button> </div> </div> </div> diff --git a/src/mol-plugin/ui/structure/util.ts b/src/mol-plugin/ui/structure/util.ts deleted file mode 100644 index ca17b74fe..000000000 --- a/src/mol-plugin/ui/structure/util.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import { StructureElement } from '../../../mol-model/structure'; -import { EmptyLoci, isEmptyLoci } from '../../../mol-model/loci'; -import { MolScriptBuilder } from '../../../mol-script/language/builder'; -import { formatMolScript } from '../../../mol-script/language/expression-formatter'; - -export function getExpression(loci: StructureElement.Loci | EmptyLoci) { - const scriptExpression = isEmptyLoci(loci) - ? MolScriptBuilder.struct.generator.empty() - : StructureElement.Loci.toScriptExpression(loci) - return formatMolScript(scriptExpression) -} \ No newline at end of file diff --git a/src/mol-plugin/util/interactivity.ts b/src/mol-plugin/util/interactivity.ts index bce545a96..80a5cc7f0 100644 --- a/src/mol-plugin/util/interactivity.ts +++ b/src/mol-plugin/util/interactivity.ts @@ -115,7 +115,7 @@ namespace Interactivity { abstract apply(e: MarkEvent): void constructor(public readonly ctx: PluginContext, props: Partial<Props> = {}) { - this.sel = ctx.helpers.structureSelection + this.sel = ctx.helpers.structureSelectionManager this.setProps(props) } } diff --git a/src/mol-plugin/util/structure-overpaint-helper.ts b/src/mol-plugin/util/structure-overpaint-helper.ts new file mode 100644 index 000000000..9bdbce04a --- /dev/null +++ b/src/mol-plugin/util/structure-overpaint-helper.ts @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { PluginStateObject } from '../../mol-plugin/state/objects'; +import { StateTransforms } from '../../mol-plugin/state/transforms'; +import { StateSelection, StateObjectCell, StateTransform, StateBuilder } from '../../mol-state'; +import { Structure, StructureElement } from '../../mol-model/structure'; +import { isEmptyLoci, EmptyLoci } from '../../mol-model/loci'; +import { PluginContext } from '../context'; +import { Color } from '../../mol-util/color'; +import { MolScriptBuilder } from '../../mol-script/language/builder'; +import { formatMolScript } from '../../mol-script/language/expression-formatter'; + +type OverpaintEachReprCallback = (update: StateBuilder.Root, repr: StateObjectCell<PluginStateObject.Molecule.Structure.Representation3D, StateTransform<typeof StateTransforms.Representation.StructureRepresentation3D>>, rootStructure: Structure, overpaint?: StateObjectCell<any, StateTransform<typeof StateTransforms.Representation.OverpaintStructureRepresentation3D>>) => void +const OverpaintManagerTag = 'overpaint-controls' + +export function getExpression(loci: StructureElement.Loci | EmptyLoci) { + const scriptExpression = isEmptyLoci(loci) + ? MolScriptBuilder.struct.generator.empty() + : StructureElement.Loci.toScriptExpression(loci) + return formatMolScript(scriptExpression) +} + +export class StructureOverpaintHelper { + private async eachRepr(callback: OverpaintEachReprCallback) { + const state = this.plugin.state.dataState; + const reprs = state.select(StateSelection.Generators.ofType(PluginStateObject.Molecule.Structure.Representation3D)); + + const update = state.build(); + for (const r of reprs) { + const overpaint = state.select(StateSelection.Generators.ofTransformer(StateTransforms.Representation.OverpaintStructureRepresentation3D, r.transform.ref).withTag(OverpaintManagerTag)); + + const structure = r.obj!.data.source.data + const rootStructure = structure.parent || structure + + callback(update, r, rootStructure, overpaint[0]) + } + + await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true })); + } + + async set(color: Color | -1, types?: string[]) { + await this.eachRepr((update, repr, rootStructure, overpaint) => { + if (types && !types.includes(repr.params!.values.type.name)) return + + const loci = this.plugin.helpers.structureSelectionManager.get(rootStructure) + if (isEmptyLoci(loci) || loci.elements.length === 0) return + const expression = getExpression(loci) + + const layer = { + script: { language: 'mol-script', expression }, + color: color === -1 ? Color(0) : color, + clear: color === -1 + } + + if (overpaint) { + update.to(overpaint).update({ layers: [ ...overpaint.params!.values.layers, layer ], alpha: 1 }) + } else { + update.to(repr.transform.ref) + .apply(StateTransforms.Representation.OverpaintStructureRepresentation3D, { layers: [ layer ], alpha: 1 }, { tags: OverpaintManagerTag }); + } + }) + } + + add(color: Color, types?: string[]) { + this.set(color, types) + } + + clear(types?: string[]) { + this.set(-1, types) + } + + clearAll() { + this.eachRepr((update, repr, rootStructure, overpaint) => { + if (overpaint) update.delete(overpaint.transform.ref) + }) + } + + constructor(private plugin: PluginContext) { + + } +} \ No newline at end of file diff --git a/src/mol-plugin/util/structure-representation-helper.ts b/src/mol-plugin/util/structure-representation-helper.ts new file mode 100644 index 000000000..4ada5f0db --- /dev/null +++ b/src/mol-plugin/util/structure-representation-helper.ts @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { PluginStateObject } from '../../mol-plugin/state/objects'; +import { StateTransforms } from '../../mol-plugin/state/transforms'; +import { StateTransformer, StateSelection, StateObjectCell, StateTransform } from '../../mol-state'; +import { StructureElement } from '../../mol-model/structure'; +import { isEmptyLoci } from '../../mol-model/loci'; +import { PluginContext } from '../context'; +import { parseMolScript } from '../../mol-script/language/parser'; +import { StructureRepresentation3DHelpers } from '../state/transforms/representation'; +import Expression from '../../mol-script/language/expression'; +import { formatMolScript } from '../../mol-script/language/expression-formatter'; +import { MolScriptBuilder as MS } from '../../mol-script/language/builder'; + +type StructureTransform = StateObjectCell<PluginStateObject.Molecule.Structure, StateTransform<StateTransformer<any, PluginStateObject.Molecule.Structure, any>>> +const RepresentationManagerTag = 'representation-controls' + +function getRepresentationManagerTag(type: string) { + return `${RepresentationManagerTag}-${type}` +} + +function getCombinedExpression(modifier: SelectionModifier, expression: Expression, currentExpression: Expression): Expression { + switch (modifier) { + case 'add': return MS.struct.combinator.merge([ currentExpression, expression ]) + case 'remove': return MS.struct.modifier.exceptBy({ 0: currentExpression, by: expression }) + case 'only': return expression + case 'all': return MS.struct.generator.all() + } +} + +type SelectionModifier = 'add' | 'remove' | 'only' | 'all' + +export class StructureRepresentationHelper { + async set(modifier: SelectionModifier, type: string, expression: Expression, structure: StructureTransform) { + const state = this.plugin.state.dataState + const update = state.build(); + const s = structure.obj!.data + + const selections = state.select(StateSelection.Generators.ofType(PluginStateObject.Molecule.Structure, structure.transform.ref).withTag(getRepresentationManagerTag(type))); + + if (selections.length > 0) { + const parsedExpressions = parseMolScript(selections[0].params!.values.query.expression) + if (parsedExpressions.length === 0) return + const currentExpression = parsedExpressions[0] + const combinedExpression = getCombinedExpression(modifier, expression, currentExpression) + + update.to(selections[0]).update({ + ...selections[0].params!.values, + query: { language: 'mol-script', expression: formatMolScript(combinedExpression) } + }) + } else { + const combinedExpression = getCombinedExpression(modifier, expression, MS.struct.generator.empty()) + + update.to(structure.transform.ref) + .apply( + StateTransforms.Model.UserStructureSelection, + { + query: { language: 'mol-script', expression: formatMolScript(combinedExpression) }, + label: type + }, + { tags: [ RepresentationManagerTag, getRepresentationManagerTag(type) ] } + ) + .apply( + StateTransforms.Representation.StructureRepresentation3D, + StructureRepresentation3DHelpers.getDefaultParams(this.plugin, type as any, s) + ) + } + + await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true })); + } + + async setSelected(modifier: SelectionModifier, type: string) { + const state = this.plugin.state.dataState; + const structures = state.select(StateSelection.Generators.rootsOfType(PluginStateObject.Molecule.Structure)); + + for (const structure of structures) { + const s = structure.obj!.data + const _loci = this.plugin.helpers.structureSelectionManager.get(s) + const loci = isEmptyLoci(_loci) ? StructureElement.Loci(s, []) : _loci + const expression = StructureElement.Loci.toScriptExpression(loci) + + await this.set(modifier, type, expression, structure) + } + } + + async hideAll(type: string) { + const state = this.plugin.state.dataState; + const update = state.build(); + + state.select(StateSelection.Generators.ofType(PluginStateObject.Molecule.Structure).withTag(getRepresentationManagerTag(type))).forEach(structure => update.delete(structure.transform.ref)); + + await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true })); + } + + constructor(private plugin: PluginContext) { + + } +} \ No newline at end of file diff --git a/src/mol-plugin/util/structure-selection-helper.ts b/src/mol-plugin/util/structure-selection-helper.ts new file mode 100644 index 000000000..f159f000a --- /dev/null +++ b/src/mol-plugin/util/structure-selection-helper.ts @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { MolScriptBuilder as MS } from '../../mol-script/language/builder'; +import { StateSelection } from '../../mol-state'; +import { PluginStateObject } from '../state/objects'; +import { QueryContext, StructureSelection, QueryFn, Queries as _Queries } from '../../mol-model/structure'; +import { compile } from '../../mol-script/runtime/query/compiler'; +import { ButtonsType } from '../../mol-util/input/input-observer'; +import { EmptyLoci } from '../../mol-model/loci'; +import { PluginContext } from '../context'; + +export const StructureSelectionQueries = { + all: () => compile<StructureSelection>(MS.struct.generator.all()), + polymers: () => _Queries.internal.atomicSequence(), + water: () => _Queries.internal.water(), + ligands: () => _Queries.internal.atomicHet(), + coarse: () => _Queries.internal.spheres(), +} + +export class StructureSelectionHelper { + select(query: QueryFn<StructureSelection>) { + const state = this.plugin.state.dataState + const structures = state.select(StateSelection.Generators.rootsOfType(PluginStateObject.Molecule.Structure)) + + for (const so of structures) { + const s = so.obj!.data + const result = query(new QueryContext(s)) + const loci = StructureSelection.toLoci2(result) + + // TODO use better API when available + this.plugin.interactivity.lociSelections.apply({ + current: { loci }, + buttons: ButtonsType.Flag.Secondary, + modifiers: { shift: false, alt: false, control: true, meta: false } + }) + } + } + + clearSelection() { + // TODO use better API when available + this.plugin.interactivity.lociSelections.apply({ + current: { loci: EmptyLoci }, + buttons: ButtonsType.Flag.Secondary, + modifiers: { shift: false, alt: false, control: true, meta: false } + }) + } + + constructor(private plugin: PluginContext) { + + } +} \ No newline at end of file -- GitLab