Browse Source

make custom structure properties auto-attachable

Alexander Rose 5 years ago
parent
commit
f893aba522

+ 36 - 25
src/mol-model-props/common/custom-property-registry.ts

@@ -73,22 +73,22 @@ export { CustomStructureProperty }
 
 namespace CustomStructureProperty {
     export interface Provider<Params extends PD.Params, Value> {
-        label: string
-        descriptor: CustomPropertyDescriptor
-        getParams: (data: Structure) => Params
-        isApplicable: (data: Structure) => boolean
-        attach: (data: Structure, props?: Partial<PD.Values<Params>>) => Task<void>
-        getValue: (data: Structure) => ValueBox<Value | undefined>
-        setProps: (data: Structure, props: PD.Values<Params>) => void
+        readonly label: string
+        readonly descriptor: CustomPropertyDescriptor
+        readonly getParams: (data: Structure) => Params
+        readonly isApplicable: (data: Structure) => boolean
+        readonly attach: (data: Structure, props?: Partial<PD.Values<Params>>) => Task<void>
+        readonly getValue: (data: Structure) => ValueBox<Value | undefined>
+        readonly setProps: (data: Structure, props: PD.Values<Params>) => void
     }
 
     export interface ProviderBuilder<Params extends PD.Params, Value> {
-        label: string
-        defaultParams: Params
-        getParams: (data: Structure) => Params
-        isApplicable: (data: Structure) => boolean
-        compute: (ctx: RuntimeContext, data: Structure, props: PD.Values<Params>) => Promise<Value>
-        descriptor: CustomPropertyDescriptor
+        readonly label: string
+        readonly descriptor: CustomPropertyDescriptor
+        readonly defaultParams: Params
+        readonly getParams: (data: Structure) => Params
+        readonly isApplicable: (data: Structure) => boolean
+        readonly compute: (ctx: RuntimeContext, data: Structure, props: PD.Values<Params>) => Promise<Value>
     }
 
     // TODO currently this always uses .inheritedPropertyData
@@ -131,7 +131,7 @@ namespace CustomStructureProperty {
                     // this invalidates property.value
                     set(data, p, undefined)
                 }
-            },
+            }
         }
     }
 
@@ -141,33 +141,44 @@ namespace CustomStructureProperty {
     }
 
     export class Registry {
-        private providers = OrderedMap<string, Provider<any, any>>().asMutable();
+        private providers = OrderedMap<string, Provider<any, any>>().asMutable()
+        private defaultAutoAttachValues = new Map<string, boolean>()
 
         /** Get params for all applicable property providers */
         getParams(data: Structure) {
             const values = this.providers.values();
-            const params: PD.Params = {};
+            const params: PD.Params = {}
             while (true) {
-                const v = values.next();
-                if (v.done) break;
-                if (!v.value.isApplicable(data)) continue;
-                params[v.value.descriptor.name] = PD.Group(v.value.getParams(data), { label: v.value.label })
+                const v = values.next()
+                if (v.done) break
+                const provider = v.value
+                if (!provider.isApplicable(data)) continue
+                params[provider.descriptor.name] = PD.Group({
+                    autoAttach: PD.Boolean(this.defaultAutoAttachValues.get(provider.descriptor.name)!),
+                    ...provider.getParams(data),
+                }, { label: v.value.label })
             }
             return params
         }
 
+        setDefaultAutoAttach(name: string, value: boolean) {
+            this.defaultAutoAttachValues.set(name, value)
+        }
+
         get(name: string) {
             const prop = this.providers.get(name);
-            if (!prop) throw new Error(`Custom property '${name}' is not registered.`);
-            return this.providers.get(name);
+            if (!prop) throw new Error(`Custom property '${name}' is not registered.`)
+            return this.providers.get(name)
         }
 
-        register(provider: Provider<any, any>) {
-            this.providers.set(provider.descriptor.name, provider);
+        register(provider: Provider<any, any>, defaultAutoAttach: boolean) {
+            this.providers.set(provider.descriptor.name, provider)
+            this.defaultAutoAttachValues.set(provider.descriptor.name, defaultAutoAttach)
         }
 
         unregister(name: string) {
-            this.providers.delete(name);
+            this.providers.delete(name)
+            this.defaultAutoAttachValues.delete(name)
         }
     }
 }

+ 43 - 5
src/mol-plugin/behavior/dynamic/custom-props/computed/accessible-surface-area.ts

@@ -5,18 +5,56 @@
  */
 
 import { PluginBehavior } from '../../../behavior';
+import { ParamDefinition as PD } from '../../../../../mol-util/param-definition';
 import { AccessibleSurfaceAreaProvider } from '../../../../../mol-model-props/computed/accessible-surface-area';
+import { Loci } from '../../../../../mol-model/loci';
 
-export const AccessibleSurfaceArea = PluginBehavior.create<{ autoAttach: boolean }>({
+export const AccessibleSurfaceArea = PluginBehavior.create<{ autoAttach: boolean, showTooltip: boolean }>({
     name: 'computed-accessible-surface-area-prop',
     category: 'custom-props',
     display: { name: 'Accessible Surface Area' },
-    ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean }> {
+    ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showTooltip: boolean }> {
+        private provider = AccessibleSurfaceAreaProvider
+
+        private label = (loci: Loci): string | undefined => {
+            if (!this.params.showTooltip) return void 0;
+
+            switch (loci.kind) {
+                case 'element-loci':
+                    if (loci.elements.length === 0) return void 0;
+                    // const e = loci.elements[0];
+                    // const u = e.unit;
+                    if (!this.provider.getValue(loci.structure).value) return;
+
+                    return `Accessible Surface Area: ${'TODO'} \u212B<sup>3</sup>`;
+
+                default: return void 0;
+            }
+        }
+
+        update(p: { autoAttach: boolean, showTooltip: boolean }) {
+            let updated = (
+                this.params.autoAttach !== p.autoAttach ||
+                this.params.showTooltip !== p.showTooltip
+            )
+            this.params.autoAttach = p.autoAttach;
+            this.params.showTooltip = p.showTooltip;
+            this.ctx.customStructureProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
+            return updated;
+        }
+
         register(): void {
-            this.ctx.customStructureProperties.register(AccessibleSurfaceAreaProvider);
+            this.ctx.customStructureProperties.register(this.provider, this.params.autoAttach);
+            this.ctx.lociLabels.addProvider(this.label);
         }
+
         unregister() {
-            this.ctx.customStructureProperties.unregister(AccessibleSurfaceAreaProvider.descriptor.name);
+            this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
+            this.ctx.lociLabels.removeProvider(this.label);
         }
-    }
+    },
+    params: () => ({
+        autoAttach: PD.Boolean(false),
+        showTooltip: PD.Boolean(true)
+    })
 });

+ 19 - 3
src/mol-plugin/behavior/dynamic/custom-props/computed/secondary-structure.ts

@@ -5,6 +5,7 @@
  */
 
 import { PluginBehavior } from '../../../behavior';
+import { ParamDefinition as PD } from '../../../../../mol-util/param-definition';
 import { SecondaryStructureProvider } from '../../../../../mol-model-props/computed/secondary-structure';
 
 export const SecondaryStructure = PluginBehavior.create<{ autoAttach: boolean }>({
@@ -12,11 +13,26 @@ export const SecondaryStructure = PluginBehavior.create<{ autoAttach: boolean }>
     category: 'custom-props',
     display: { name: 'Secondary Structure' },
     ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean }> {
+        private provider = SecondaryStructureProvider
+
+        update(p: { autoAttach: boolean, showTooltip: boolean }) {
+            let updated = (
+                this.params.autoAttach !== p.autoAttach
+            )
+            this.params.autoAttach = p.autoAttach;
+            this.ctx.customStructureProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
+            return updated;
+        }
+
         register(): void {
-            this.ctx.customStructureProperties.register(SecondaryStructureProvider);
+            this.ctx.customStructureProperties.register(this.provider, this.params.autoAttach);
         }
+
         unregister() {
-            this.ctx.customStructureProperties.unregister(SecondaryStructureProvider.descriptor.name);
+            this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
         }
-    }
+    },
+    params: () => ({
+        autoAttach: PD.Boolean(false)
+    })
 });

+ 7 - 2
src/mol-plugin/state/transforms/model.ts

@@ -666,10 +666,15 @@ const CustomStructureProperties = PluginStateTransform.BuiltIn({
         });
     }
 });
-async function attachStructureProps(structure: Structure, ctx: PluginContext, taskCtx: RuntimeContext, params: {}) {
+async function attachStructureProps(structure: Structure, ctx: PluginContext, taskCtx: RuntimeContext, params: PD.Values<PD.Params>) {
     for (const name of Object.keys(params)) {
         const property = ctx.customStructureProperties.get(name)
-        property.setProps(structure, params[name as keyof typeof params])
+        const props = params[name as keyof typeof params]
+        if (props.autoAttach) {
+            await property.attach(structure, props).runInContext(taskCtx)
+        } else {
+            property.setProps(structure, props)
+        }
     }
 }
 

+ 0 - 1
src/mol-theme/color/accessible-surface-area.ts

@@ -38,7 +38,6 @@ export function AccessibleSurfaceAreaColorTheme(ctx: ThemeDataContext, props: PD
 
     const { label_comp_id } = StructureProperties.residue
     const accessibleSurfaceArea = ctx.structure && AccessibleSurfaceAreaProvider.getValue(ctx.structure)
-    // use `accessibleSurfaceArea.id` as context hash, when available
     const contextHash = accessibleSurfaceArea?.version
 
     if (accessibleSurfaceArea && accessibleSurfaceArea.value && ctx.structure) {