From ff8fec542c75114cbb387560ab1d6ac563167130 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Tue, 23 Jul 2019 12:04:02 -0700 Subject: [PATCH] wip, overpaint controls, overpaint clearing --- src/mol-geo/geometry/overpaint-data.ts | 1 + src/mol-plugin/state/transforms/helpers.ts | 6 +- .../state/transforms/representation.ts | 30 ++---- src/mol-plugin/ui/controls.tsx | 93 +++++++++---------- src/mol-repr/visual.ts | 8 +- src/mol-theme/overpaint.ts | 3 +- 6 files changed, 66 insertions(+), 75 deletions(-) diff --git a/src/mol-geo/geometry/overpaint-data.ts b/src/mol-geo/geometry/overpaint-data.ts index b97e19a4a..08e753594 100644 --- a/src/mol-geo/geometry/overpaint-data.ts +++ b/src/mol-geo/geometry/overpaint-data.ts @@ -25,6 +25,7 @@ export function applyOverpaintColor(array: Uint8Array, start: number, end: numbe export function clearOverpaint(array: Uint8Array, start: number, end: number) { array.fill(0, start * 4, end * 4) + return true } export function createOverpaint(count: number, overpaintData?: OverpaintData): OverpaintData { diff --git a/src/mol-plugin/state/transforms/helpers.ts b/src/mol-plugin/state/transforms/helpers.ts index 509c96410..a68fe1cd0 100644 --- a/src/mol-plugin/state/transforms/helpers.ts +++ b/src/mol-plugin/state/transforms/helpers.ts @@ -25,11 +25,11 @@ function scriptToLoci(structure: Structure, script: Script) { return StructureSelection.toLoci2(result) } -export function getStructureOverpaint(structure: Structure, scriptLayers: { script: Script, color: Color }[], alpha: number): Overpaint { +export function getStructureOverpaint(structure: Structure, scriptLayers: { script: Script, color: Color, clear: boolean }[], alpha: number): Overpaint { const layers: Overpaint.Layer[] = [] for (let i = 0, il = scriptLayers.length; i < il; ++i) { - const { script, color } = scriptLayers[i] - layers.push({ loci: scriptToLoci(structure, script), color }) + const { script, color, clear } = scriptLayers[i] + layers.push({ loci: scriptToLoci(structure, script), color, clear }) } return { layers, alpha } } diff --git a/src/mol-plugin/state/transforms/representation.ts b/src/mol-plugin/state/transforms/representation.ts index 9cf1b7338..35ede9697 100644 --- a/src/mol-plugin/state/transforms/representation.ts +++ b/src/mol-plugin/state/transforms/representation.ts @@ -333,25 +333,15 @@ const OverpaintStructureRepresentation3D = PluginStateTransform.BuiltIn({ to: SO.Molecule.Structure.Representation3DState, params: { layers: PD.ObjectList({ - script: PD.ScriptExpression({ language: 'mol-script', expression: '(sel.atom.atom-groups :residue-test (= atom.resname LYS))' }), - color: PD.Color(ColorNames.blueviolet) - }, e => `${Color.toRgbString(e.color)}`, { - defaultValue: [ - { - script: { - language: 'mol-script', - expression: '(sel.atom.atom-groups :residue-test (= atom.resname LYS))' - }, - color: ColorNames.blueviolet - }, - { - script: { - language: 'mol-script', - expression: '(sel.atom.atom-groups :residue-test (= atom.resname ALA))' - }, - color: ColorNames.chartreuse - } - ] + script: PD.ScriptExpression({ language: 'mol-script', expression: '(sel.atom.all)' }), + color: PD.Color(ColorNames.blueviolet), + clear: PD.Boolean(false) + }, e => `${e.clear ? 'Clear' : Color.toRgbString(e.color)}`, { + defaultValue: [{ + script: { language: 'mol-script', expression: '(sel.atom.all)' }, + color: ColorNames.blueviolet, + clear: false + }] }), alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity' }), } @@ -391,7 +381,7 @@ const TransparencyStructureRepresentation3D = PluginStateTransform.BuiltIn({ from: SO.Molecule.Structure.Representation3D, to: SO.Molecule.Structure.Representation3DState, params: { - script: PD.ScriptExpression({ language: 'mol-script', expression: '(sel.atom.atom-groups :chain-test (= atom.label_asym_id A))' }), + script: PD.ScriptExpression({ language: 'mol-script', expression: '(sel.atom.all)' }), value: PD.Numeric(0.75, { min: 0, max: 1, step: 0.01 }, { label: 'Transparency' }), variant: PD.Select('single', [['single', 'Single-layer'], ['multi', 'Multi-layer']]) } diff --git a/src/mol-plugin/ui/controls.tsx b/src/mol-plugin/ui/controls.tsx index 972d6596b..777d2eb9d 100644 --- a/src/mol-plugin/ui/controls.tsx +++ b/src/mol-plugin/ui/controls.tsx @@ -13,7 +13,7 @@ import { LociLabelEntry } from '../../mol-plugin/util/loci-label-manager'; import { IconButton, Icon } from './controls/common'; import { PluginStateObject } from '../../mol-plugin/state/objects'; import { StateTransforms } from '../../mol-plugin/state/transforms'; -import { StateTransformer, StateSelection } from '../../mol-state'; +import { StateTransformer, StateSelection, StateObjectCell, StateTransform, StateBuilder } from '../../mol-state'; import { ModelFromTrajectory } from '../../mol-plugin/state/transforms/model'; import { AnimationControls } from './state/animation'; import { ParamDefinition as PD} from '../../mol-util/param-definition'; @@ -260,61 +260,62 @@ export class LociLabelControl extends PluginUIComponent<{}, { entries: ReadonlyA } } +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 class OverpaintControls extends PluginUIComponent<{}, { params: PD.Values<typeof OverpaintControls.Params> }> { state = { params: PD.getDefaultValues(OverpaintControls.Params) } static Params = { - color: PD.Color(ColorNames.cyan) + color: PD.Color(ColorNames.cyan), }; - private layers = new Map<Structure, Map<string, { script: { language: string, expression: string }, color: Color }>>() + private layers = new Map<Structure, Map<string, { script: { language: string, expression: string }, color: Color, clear: boolean }>>() componentDidMount() { - // TODO handle Representation3D object creation this.subscribe(this.plugin.events.state.object.created, ({ ref, state }) => { this.sync() }); } - sync = async () => { + 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('overpaint-manager')); + 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 - const layers = this.layers.get(rootStructure) - if (!layers) continue - - const props = { layers: Array.from(layers.values()), alpha: 1 } - - if (overpaint.length > 0) { - update.to(overpaint[0]).update(props) - } else { - update.to(r.transform.ref) - .apply(StateTransforms.Representation.OverpaintStructureRepresentation3D, props, { tags: 'overpaint-manager' }); - } + callback(update, r, rootStructure, overpaint[0]) } await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true })); } - add = async () => { - const state = this.plugin.state.dataState; - const reprs = state.select(StateSelection.Generators.ofType(PluginStateObject.Molecule.Structure.Representation3D)); + sync = async () => { + await this.eachRepr((update, repr, rootStructure, overpaint) => { + const layers = this.layers.get(rootStructure) + if (!layers) return - const update = state.build(); - for (const r of reprs) { - const overpaint = state.select(StateSelection.Generators.ofTransformer(StateTransforms.Representation.OverpaintStructureRepresentation3D, r.transform.ref).withTag('overpaint-manager')); + const props = { layers: Array.from(layers.values()), alpha: 1 } - const structure = r.obj!.data.source.data - const rootStructure = structure.parent || structure + if (overpaint) { + update.to(overpaint).update(props) + } else { + update.to(repr.transform.ref) + .apply(StateTransforms.Representation.OverpaintStructureRepresentation3D, props, { tags: OverpaintManagerTag }); + } + }) + } + set = async (clear: boolean) => { + await this.eachRepr((update, repr, rootStructure, overpaint) => { const loci = this.plugin.helpers.structureSelection.get(rootStructure) + if (isEmptyLoci(loci) || loci.elements.length === 0) return + const scriptExpression = isEmptyLoci(loci) ? MolScriptBuilder.struct.generator.empty() : StructureElement.Loci.toScriptExpression(loci) @@ -323,39 +324,35 @@ export class OverpaintControls extends PluginUIComponent<{}, { params: PD.Values if (!this.layers.has(rootStructure)) this.layers.set(rootStructure, new Map()) const layers = this.layers.get(rootStructure)! - layers.set(`${this.state.params.color}|${expression}`, { + layers.set(`${this.state.params.color}|${clear}|${expression}`, { script: { language: 'mol-script', expression }, - color: this.state.params.color + color: this.state.params.color, + clear }) const props = { layers: Array.from(layers.values()), alpha: 1 } - if (overpaint.length > 0) { - update.to(overpaint[0]).update(props) + if (overpaint) { + update.to(overpaint).update(props) } else { - update.to(r.transform.ref) - .apply(StateTransforms.Representation.OverpaintStructureRepresentation3D, props, { tags: 'overpaint-manager' }); + update.to(repr.transform.ref) + .apply(StateTransforms.Representation.OverpaintStructureRepresentation3D, props, { tags: OverpaintManagerTag }); } - } + }) + } - await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true })); + add = async () => { + this.set(false) } - clearAll = async () => { - const state = this.plugin.state.dataState; - const reprs = state.select(StateSelection.Generators.ofType(PluginStateObject.Molecule.Structure.Representation3D)); + clear = async () => { + this.set(true) + } + clearAll = async () => { this.layers.clear() - - const update = state.build(); - for (const r of reprs) { - const overpaint = state.select(StateSelection.Generators.ofTransformer(StateTransforms.Representation.OverpaintStructureRepresentation3D, r.transform.ref).withTag('overpaint-manager')); - - if (overpaint.length > 0) { - update.to(overpaint[0]).update({ layers: [], alpha: 1 }) - } - } - - await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true })); + await this.eachRepr((update, repr, rootStructure, overpaint) => { + if (overpaint) update.delete(overpaint.transform.ref) + }) } render() { @@ -371,7 +368,7 @@ export class OverpaintControls extends PluginUIComponent<{}, { params: PD.Values <div className='msp-btn-row-group'> <button className='msp-btn msp-btn-block msp-form-control' onClick={this.add}>Add</button> - {/* <button className='msp-btn msp-btn-block msp-form-control' onClick={this.add}>Clear</button> */} + <button className='msp-btn msp-btn-block msp-form-control' onClick={this.clear}>Clear</button> <button className='msp-btn msp-btn-block msp-form-control' onClick={this.clearAll}>Clear All</button> </div> </div> diff --git a/src/mol-repr/visual.ts b/src/mol-repr/visual.ts index 1b6f2530b..263dcaada 100644 --- a/src/mol-repr/visual.ts +++ b/src/mol-repr/visual.ts @@ -82,15 +82,17 @@ namespace Visual { // ensure texture has right size createOverpaint(overpaint.layers.length ? count : 0, renderObject.values) - // clear if requested + // clear all if requested if (clear) clearOverpaint(tOverpaint.ref.value.array, 0, count) for (let i = 0, il = overpaint.layers.length; i < il; ++i) { - const { loci, color } = overpaint.layers[i] + const { loci, color, clear } = overpaint.layers[i] const apply = (interval: Interval) => { const start = Interval.start(interval) const end = Interval.end(interval) - return applyOverpaintColor(tOverpaint.ref.value.array, start, end, color, overpaint.alpha) + return clear + ? clearOverpaint(tOverpaint.ref.value.array, start, end) + : applyOverpaintColor(tOverpaint.ref.value.array, start, end, color, overpaint.alpha) } lociApply(loci, apply) } diff --git a/src/mol-theme/overpaint.ts b/src/mol-theme/overpaint.ts index 9f566a056..d885a5de8 100644 --- a/src/mol-theme/overpaint.ts +++ b/src/mol-theme/overpaint.ts @@ -12,7 +12,7 @@ export { Overpaint } type Overpaint = { layers: ReadonlyArray<Overpaint.Layer>, readonly alpha: number } namespace Overpaint { - export type Layer = { readonly loci: Loci, readonly color: Color } + export type Layer = { readonly loci: Loci, readonly color: Color, readonly clear: boolean } export const Empty: Overpaint = { layers: [], alpha: 1 } export function areEqual(oA: Overpaint, oB: Overpaint) { @@ -20,6 +20,7 @@ namespace Overpaint { if (oA.layers.length !== oB.layers.length) return false if (oA.alpha !== oB.alpha) return false for (let i = 0, il = oA.layers.length; i < il; ++i) { + if (oA.layers[i].clear !== oB.layers[i].clear) return false if (oA.layers[i].color !== oB.layers[i].color) return false if (!Loci.areEqual(oA.layers[i].loci, oB.layers[i].loci)) return false } -- GitLab