Ver Fonte

wip, overpaint controls, overpaint clearing

Alexander Rose há 5 anos atrás
pai
commit
ff8fec542c

+ 1 - 0
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 {

+ 3 - 3
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 }
 }

+ 10 - 20
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']])
     }

+ 45 - 48
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>

+ 5 - 3
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)
         }

+ 2 - 1
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
         }