فهرست منبع

[wip] mol-plugin: measurements

David Sehnal 5 سال پیش
والد
کامیت
65c2faaced

+ 3 - 1
src/mol-plugin/state/objects.ts

@@ -7,7 +7,7 @@
 
 import { CifFile } from '../../mol-io/reader/cif';
 import { PlyFile } from '../../mol-io/reader/ply/schema';
-import { Model as _Model, Structure as _Structure } from '../../mol-model/structure';
+import { Model as _Model, Structure as _Structure, StructureElement } from '../../mol-model/structure';
 import { VolumeData } from '../../mol-model/volume';
 import { PluginBehavior } from '../../mol-plugin/behavior/behavior';
 import { Representation } from '../../mol-repr/representation';
@@ -98,6 +98,8 @@ export namespace PluginStateObject {
                 info?: unknown
             }
             export class Representation3DState extends Create<Representation3DStateData>({ name: 'Structure 3D State', typeClass: 'Object' }) { }
+
+            export class Selections extends Create<ReadonlyArray<StructureElement.Loci>>({ name: 'Selections', typeClass: 'Object' }) {}
         }
     }
 

+ 64 - 0
src/mol-plugin/state/transforms/model.ts

@@ -668,4 +668,68 @@ const ShapeFromPly = PluginStateTransform.BuiltIn({
             return new SO.Shape.Provider(shape, props);
         });
     }
+});
+
+export { MultiStructureSelection }
+type MultiStructureSelectionCacheEntry = {
+    ref: string,
+    expression: Expression,
+    compiled: QueryFn<Sel>,
+    source: Structure
+}
+type MultiStructureSelection = typeof MultiStructureSelection
+const MultiStructureSelection = PluginStateTransform.BuiltIn({
+    name: 'structure-multi-selection-from-expression',
+    display: { name: 'Multi-structure Selection', description: 'Create selection object from multiple structures.' },
+    from: SO.Root,
+    to: SO.Molecule.Structure.Selections,
+    params: {
+        selections: PD.ObjectList({
+            ref: PD.Text(),
+            expression: PD.Value<Expression>(MolScriptBuilder.struct.generator.empty)
+        }, e => e.ref, { isHidden: true }),
+        label: PD.Optional(PD.Text('', { isHidden: true }))
+    }
+})({
+    apply({ params, cache, dependencies }) {
+        const queries: MultiStructureSelectionCacheEntry[] = [];
+        const loci: StructureElement.Loci[] = [];
+        let size = 0;
+
+        for (const sel of params.selections) {
+            const e: MultiStructureSelectionCacheEntry = {
+                ref: sel.ref,
+                expression: sel.expression,
+                compiled: compile<Sel>(sel.expression),
+                source: dependencies![sel.ref].data as Structure
+            };
+            queries.push(e);
+
+            const s = e.compiled(new QueryContext(e.source));
+            const l = Sel.toLociWithSourceUnits(s);
+            loci.push(l);
+            size += StructureElement.Loci.size(l);
+        }
+
+        (cache as object as any).queries = queries;
+
+        console.log(loci);
+
+        const props = { label: `${params.label || 'Multi-selection'}`, description: `${params.selections} source(s), ${size} element(s) total` };
+        return new SO.Molecule.Structure.Selections(loci, props);
+    },
+    // TODO: implement this
+    // update: ({ a, b, oldParams, newParams, cache }) => {
+    //     if (oldParams.expression !== newParams.expression) return StateTransformer.UpdateResult.Recreate;
+
+    //     if ((cache as { source: Structure }).source === a.data) {
+    //         return StateTransformer.UpdateResult.Unchanged;
+    //     }
+    //     (cache as { source: Structure }).source = a.data;
+
+    //     if (updateStructureFromQuery((cache as { compiled: QueryFn<Sel> }).compiled, a.data, b, newParams.label)) {
+    //         return StateTransformer.UpdateResult.Updated;
+    //     }
+    //     return StateTransformer.UpdateResult.Null;
+    // }
 });

+ 11 - 2
src/mol-plugin/util/structure-measurement.ts

@@ -9,6 +9,7 @@ import { PluginContext } from '../context';
 import { StateSelection, StateTransform } from '../../mol-state';
 import { StateTransforms } from '../state/transforms';
 import { PluginCommands } from '../command';
+import { arraySetAdd } from '../../mol-util/array';
 
 export { StructureMeasurementManager }
 
@@ -30,9 +31,17 @@ class StructureMeasurementManager {
 
         if (!cellA || !cellB) return;
 
-        const update = this.getGroup();
+        const dependsOn = [cellA.transform.ref];
+        arraySetAdd(dependsOn, cellB.transform.ref);
 
-        console.log({ cellA, cellB });
+        const update = this.getGroup();
+        update.apply(StateTransforms.Model.MultiStructureSelection, {
+            selections: [
+                { ref: cellA.transform.ref, expression: StructureElement.Loci.toExpression(a) },
+                { ref: cellB.transform.ref, expression: StructureElement.Loci.toExpression(b) }
+            ],
+            label: 'Distance'
+        }, { dependsOn });
 
         const state = this.context.state.dataState;
         await PluginCommands.State.Update.dispatch(this.context, { state, tree: update, options: { doNotLogTiming: true } });

+ 3 - 3
src/mol-state/object.ts

@@ -71,9 +71,9 @@ interface StateObjectCell<T extends StateObject = StateObject, F extends StateTr
         values: any
     } | undefined,
 
-    links: {
-        from: StateObjectCell[],
-        to: StateObjectCell[]
+    dependencies: {
+        dependentBy: StateObjectCell[],
+        dependsOn: StateObjectCell[]
     },
 
     errorText?: string,

+ 26 - 11
src/mol-state/state.ts

@@ -230,7 +230,7 @@ class State {
                 definition: {},
                 values: {}
             },
-            links: { from: [], to: [] },
+            dependencies: { dependentBy: [], dependsOn: [] },
             cache: { }
         });
 
@@ -462,8 +462,8 @@ function initCellStatus(ctx: UpdateContext, roots: Ref[]) {
 }
 
 function unlinkCell(cell: StateObjectCell) {
-    for (const other of cell.links.to) {
-        arraySetRemove(other.links.from, cell);
+    for (const other of cell.dependencies.dependsOn) {
+        arraySetRemove(other.dependencies.dependentBy, cell);
     }
 }
 
@@ -484,7 +484,7 @@ function addCellsVisitor(transform: StateTransform, _: any, { ctx, added, visite
         state: { ...transform.state },
         errorText: void 0,
         params: void 0,
-        links: { from: [], to: [] },
+        dependencies: { dependentBy: [], dependsOn: [] },
         cache: void 0
     };
 
@@ -504,8 +504,8 @@ function linkCells(target: StateObjectCell, ctx: UpdateContext) {
         }
 
         const cell = ctx.cells.get(ref)!;
-        arraySetAdd(target.links.to, cell);
-        arraySetAdd(cell.links.from, target);
+        arraySetAdd(target.dependencies.dependsOn, cell);
+        arraySetAdd(cell.dependencies.dependentBy, target);
     }
 }
 
@@ -527,11 +527,11 @@ function initCells(ctx: UpdateContext, roots: Ref[]) {
     // Find dependent cells
     initCtx.visited.forEach(ref => {
         const cell = ctx.cells.get(ref)!;
-        for (const on of cell.links.to) {
-            if (initCtx.visited.has(on.transform.ref)) continue;
+        for (const by of cell.dependencies.dependentBy) {
+            if (initCtx.visited.has(by.transform.ref)) continue;
 
             if (!dependent) dependent = UniqueArray.create();
-            UniqueArray.add(dependent, on.transform.ref, on);
+            UniqueArray.add(dependent, by.transform.ref, by);
         }
     });
 
@@ -742,7 +742,22 @@ function runTask<T>(t: T | Task<T>, ctx: RuntimeContext) {
 
 function createObject(ctx: UpdateContext, cell: StateObjectCell, transformer: StateTransformer, a: StateObject, params: any) {
     if (!cell.cache) cell.cache = Object.create(null);
-    return runTask(transformer.definition.apply({ a, params, cache: cell.cache, spine: ctx.spine }, ctx.parent.globalContext), ctx.taskCtx);
+    return runTask(transformer.definition.apply({ a, params, cache: cell.cache, spine: ctx.spine, dependencies: resolveDependencies(ctx, cell) }, ctx.parent.globalContext), ctx.taskCtx);
+}
+
+function resolveDependencies(ctx: UpdateContext, cell: StateObjectCell) {
+    if (cell.dependencies.dependsOn.length === 0) return void 0;
+
+    const deps = Object.create(null);
+
+    for (const dep of cell.dependencies.dependsOn) {
+        if (!dep.obj) {
+            throw new Error('Unresolved dependency.');
+        }
+        deps[dep.transform.ref] = dep.obj;
+    }
+
+    return deps;
 }
 
 async function updateObject(ctx: UpdateContext, cell: StateObjectCell,  transformer: StateTransformer, a: StateObject, b: StateObject, oldParams: any, newParams: any) {
@@ -750,5 +765,5 @@ async function updateObject(ctx: UpdateContext, cell: StateObjectCell,  transfor
         return StateTransformer.UpdateResult.Recreate;
     }
     if (!cell.cache) cell.cache = Object.create(null);
-    return runTask(transformer.definition.update({ a, oldParams, b, newParams, cache: cell.cache, spine: ctx.spine }, ctx.parent.globalContext), ctx.taskCtx);
+    return runTask(transformer.definition.update({ a, oldParams, b, newParams, cache: cell.cache, spine: ctx.spine, dependencies: resolveDependencies(ctx, cell) }, ctx.parent.globalContext), ctx.taskCtx);
 }

+ 4 - 2
src/mol-state/transformer.ts

@@ -40,7 +40,8 @@ namespace Transformer {
         params: P,
         /** A cache object that is purged each time the corresponding StateObject is removed or recreated. */
         cache: unknown,
-        spine: StateTreeSpine
+        spine: StateTreeSpine,
+        dependencies?: { [k: string]: StateObject }
     }
 
     export interface UpdateParams<A extends StateObject = StateObject, B extends StateObject = StateObject, P extends {} = {}> {
@@ -50,7 +51,8 @@ namespace Transformer {
         newParams: P,
         /** A cache object that is purged each time the corresponding StateObject is removed or recreated. */
         cache: unknown,
-        spine: StateTreeSpine
+        spine: StateTreeSpine,
+        dependencies?: { [k: string]: StateObject }
     }
 
     export interface AutoUpdateParams<A extends StateObject = StateObject, B extends StateObject = StateObject, P extends {} = {}> {