Bladeren bron

mol-state: added StateAction

David Sehnal 6 jaren geleden
bovenliggende
commit
8b2fb2be77

+ 1 - 1
src/mol-plugin/behavior/behavior.ts

@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { PluginStateTransform, PluginStateObject } from '../state/base';
+import { PluginStateTransform, PluginStateObject } from '../state/objects';
 import { Transformer } from 'mol-state';
 import { Task } from 'mol-task';
 import { PluginContext } from 'mol-plugin/context';

+ 1 - 1
src/mol-plugin/behavior/built-in/representation.ts

@@ -5,7 +5,7 @@
  */
 
 import { PluginBehavior } from '../behavior';
-import { PluginStateObject as SO } from '../../state/base';
+import { PluginStateObject as SO } from '../../state/objects';
 import { EmptyLoci, Loci, areLociEqual } from 'mol-model/loci';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
 

+ 5 - 6
src/mol-plugin/context.ts

@@ -7,8 +7,7 @@
 import { Transformer, Transform, State } from 'mol-state';
 import { Canvas3D } from 'mol-canvas3d/canvas3d';
 import { StateTransforms } from './state/transforms';
-import { PluginStateObject as PSO } from './state/base';
-import { PluginStateObjects as SO } from './state/objects';
+import { PluginStateObject as SO } from './state/objects';
 import { RxEventHelper } from 'mol-util/rx-event-helper';
 import { PluginState } from './state';
 import { MolScriptBuilder } from 'mol-script/language/builder';
@@ -135,19 +134,19 @@ export class PluginContext {
 
     private initEvents() {
         merge(this.events.state.data.object.created, this.events.state.behavior.object.created).subscribe(o => {
-            if (!PSO.isBehavior(o.obj)) return;
+            if (!SO.isBehavior(o.obj)) return;
             console.log('registering behavior', o.obj.label);
             o.obj.data.register();
         });
 
         merge(this.events.state.data.object.removed, this.events.state.behavior.object.removed).subscribe(o => {
-            if (!PSO.isBehavior(o.obj)) return;
+            if (!SO.isBehavior(o.obj)) return;
             o.obj.data.unregister();
         });
 
         merge(this.events.state.data.object.replaced, this.events.state.behavior.object.replaced).subscribe(o => {
-            if (o.oldObj && PSO.isBehavior(o.oldObj)) o.oldObj.data.unregister();
-            if (o.newObj && PSO.isBehavior(o.newObj)) o.newObj.data.register();
+            if (o.oldObj && SO.isBehavior(o.oldObj)) o.oldObj.data.unregister();
+            if (o.newObj && SO.isBehavior(o.newObj)) o.newObj.data.register();
         });
     }
 

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

@@ -5,7 +5,7 @@
  */
 
 import { State } from 'mol-state';
-import { PluginStateObjects as SO } from './state/objects';
+import { PluginStateObject as SO } from './state/objects';
 import { Camera } from 'mol-canvas3d/camera';
 import { PluginBehavior } from './behavior';
 

+ 0 - 7
src/mol-plugin/state/action.ts

@@ -1,7 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-// TODO actions that modify state and can be "applied" to certain state objects.

+ 0 - 40
src/mol-plugin/state/base.ts

@@ -1,40 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { StateObject, Transformer } from 'mol-state';
-import { Representation } from 'mol-repr';
-import { PluginBehavior } from 'mol-plugin/behavior/behavior';
-
-export type TypeClass = 'root' | 'data' | 'prop'
-
-export namespace PluginStateObject {
-    export type Any = StateObject<any, TypeInfo>
-
-    export type TypeClass = 'Root' | 'Group' | 'Data' | 'Object' | 'Representation3D' | 'Behavior'
-    export interface TypeInfo { name: string, typeClass: TypeClass }
-
-    export const Create = StateObject.factory<TypeInfo>();
-
-    export function isRepresentation3D(o?: Any): o is StateObject<Representation.Any, TypeInfo> {
-        return !!o && o.type.typeClass === 'Representation3D';
-    }
-
-    export function isBehavior(o?: Any): o is StateObject<PluginBehavior, TypeInfo> {
-        return !!o && o.type.typeClass === 'Behavior';
-    }
-
-    export function CreateRepresentation3D<T extends Representation.Any>(type: { name: string }) {
-        return Create<T>({ ...type, typeClass: 'Representation3D' })
-    }
-
-    export function CreateBehavior<T extends PluginBehavior>(type: { name: string }) {
-        return Create<T>({ ...type, typeClass: 'Behavior' })
-    }
-}
-
-export namespace PluginStateTransform {
-    export const Create = Transformer.factory('ms-plugin');
-}

+ 46 - 20
src/mol-plugin/state/objects.ts

@@ -4,44 +4,70 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { PluginStateObject } from './base';
 import { CifFile } from 'mol-io/reader/cif';
-import { Model as _Model, Structure as _Structure } from 'mol-model/structure'
+import { Model as _Model, Structure as _Structure } from 'mol-model/structure';
+import { VolumeData } from 'mol-model/volume';
+import { PluginBehavior } from 'mol-plugin/behavior/behavior';
+import { Representation } from 'mol-repr';
 import { StructureRepresentation } from 'mol-repr/structure/index';
 import { VolumeRepresentation } from 'mol-repr/volume';
-import { VolumeData } from 'mol-model/volume';
+import { StateObject, Transformer } from 'mol-state';
+
+export type TypeClass = 'root' | 'data' | 'prop'
+
+export namespace PluginStateObject {
+    export type Any = StateObject<any, TypeInfo>
+
+    export type TypeClass = 'Root' | 'Group' | 'Data' | 'Object' | 'Representation3D' | 'Behavior'
+    export interface TypeInfo { name: string, typeClass: TypeClass }
+
+    export const Create = StateObject.factory<TypeInfo>();
+
+    export function isRepresentation3D(o?: Any): o is StateObject<Representation.Any, TypeInfo> {
+        return !!o && o.type.typeClass === 'Representation3D';
+    }
 
-const _create = PluginStateObject.Create, _createRepr3D = PluginStateObject.CreateRepresentation3D
+    export function isBehavior(o?: Any): o is StateObject<PluginBehavior, TypeInfo> {
+        return !!o && o.type.typeClass === 'Behavior';
+    }
 
-namespace PluginStateObjects {
-    export class Root extends _create({ name: 'Root', typeClass: 'Root' }) { }
+    export function CreateRepresentation3D<T extends Representation.Any>(type: { name: string }) {
+        return Create<T>({ ...type, typeClass: 'Representation3D' })
+    }
 
-    export class Group extends _create({ name: 'Group', typeClass: 'Group' }) { }
+    export function CreateBehavior<T extends PluginBehavior>(type: { name: string }) {
+        return Create<T>({ ...type, typeClass: 'Behavior' })
+    }
+
+    export class Root extends Create({ name: 'Root', typeClass: 'Root' }) { }
+
+    export class Group extends Create({ name: 'Group', typeClass: 'Group' }) { }
 
     export namespace Data {
-        export class String extends _create<string>({ name: 'String Data', typeClass: 'Data', }) { }
-        export class Binary extends _create<Uint8Array>({ name: 'Binary Data', typeClass: 'Data' }) { }
-        export class Json extends _create<any>({ name: 'JSON Data', typeClass: 'Data' }) { }
-        export class Cif extends _create<CifFile>({ name: 'CIF File', typeClass: 'Data' }) { }
+        export class String extends Create<string>({ name: 'String Data', typeClass: 'Data', }) { }
+        export class Binary extends Create<Uint8Array>({ name: 'Binary Data', typeClass: 'Data' }) { }
+        export class Json extends Create<any>({ name: 'JSON Data', typeClass: 'Data' }) { }
+        export class Cif extends Create<CifFile>({ name: 'CIF File', typeClass: 'Data' }) { }
 
         // TODO
-        // export class MultipleRaw extends _create<{
+        // export class MultipleRaw extends Create<{
         //     [key: string]: { type: 'String' | 'Binary', data: string | Uint8Array }
         // }>({ name: 'Data', typeClass: 'Data', shortName: 'MD', description: 'Multiple Keyed Data.' }) { }
     }
 
     export namespace Molecule {
-        export class Trajectory extends _create<ReadonlyArray<_Model>>({ name: 'Trajectory', typeClass: 'Object' }) { }
-        export class Model extends _create<_Model>({ name: 'Model', typeClass: 'Object' }) { }
-        export class Structure extends _create<_Structure>({ name: 'Structure', typeClass: 'Object' }) { }
-        export class Representation3D extends _createRepr3D<StructureRepresentation<any>>({ name: 'Structure 3D' }) { }
+        export class Trajectory extends Create<ReadonlyArray<_Model>>({ name: 'Trajectory', typeClass: 'Object' }) { }
+        export class Model extends Create<_Model>({ name: 'Model', typeClass: 'Object' }) { }
+        export class Structure extends Create<_Structure>({ name: 'Structure', typeClass: 'Object' }) { }
+        export class Representation3D extends CreateRepresentation3D<StructureRepresentation<any>>({ name: 'Structure 3D' }) { }
     }
 
     export namespace Volume {
-        export class Data extends _create<VolumeData>({ name: 'Volume Data', typeClass: 'Object' }) { }
-        export class Representation3D extends _createRepr3D<VolumeRepresentation<any>>({ name: 'Volume 3D' }) { }
+        export class Data extends Create<VolumeData>({ name: 'Volume Data', typeClass: 'Object' }) { }
+        export class Representation3D extends CreateRepresentation3D<VolumeRepresentation<any>>({ name: 'Volume 3D' }) { }
     }
-
 }
 
-export { PluginStateObjects }
+export namespace PluginStateTransform {
+    export const Create = Transformer.factory('ms-plugin');
+}

+ 2 - 2
src/mol-plugin/state/transforms/data.ts

@@ -4,8 +4,8 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { PluginStateTransform } from '../base';
-import { PluginStateObjects as SO } from '../objects';
+import { PluginStateTransform } from '../objects';
+import { PluginStateObject as SO } from '../objects';
 import { Task } from 'mol-task';
 import CIF from 'mol-io/reader/cif'
 import { PluginContext } from 'mol-plugin/context';

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

@@ -4,8 +4,8 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { PluginStateTransform } from '../base';
-import { PluginStateObjects as SO } from '../objects';
+import { PluginStateTransform } from '../objects';
+import { PluginStateObject as SO } from '../objects';
 import { Task } from 'mol-task';
 import { Model, Format, Structure, ModelSymmetry, StructureSymmetry, QueryContext, StructureSelection } from 'mol-model/structure';
 import { ParamDefinition as PD } from 'mol-util/param-definition';

+ 2 - 3
src/mol-plugin/state/transforms/visuals.ts

@@ -6,9 +6,8 @@
 
 import { Transformer } from 'mol-state';
 import { Task } from 'mol-task';
-import { PluginStateTransform } from '../base';
-import { PluginStateObjects as SO } from '../objects';
-//import { CartoonRepresentation, DefaultCartoonProps } from 'mol-repr/structure/representation/cartoon';
+import { PluginStateTransform } from '../objects';
+import { PluginStateObject as SO } from '../objects';
 import { BallAndStickRepresentation, DefaultBallAndStickProps } from 'mol-repr/structure/representation/ball-and-stick';
 import { PluginContext } from 'mol-plugin/context';
 

+ 1 - 1
src/mol-plugin/ui/state-tree.tsx

@@ -6,7 +6,7 @@
 
 import * as React from 'react';
 import { PluginContext } from '../context';
-import { PluginStateObject } from 'mol-plugin/state/base';
+import { PluginStateObject } from 'mol-plugin/state/objects';
 import { State } from 'mol-state'
 import { PluginCommands } from 'mol-plugin/command';
 

+ 65 - 0
src/mol-state/action.ts

@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { Task } from 'mol-task';
+import { UUID } from 'mol-util';
+import { ParamDefinition as PD } from 'mol-util/param-definition';
+import { StateObject, StateObjectCell } from './object';
+import { State } from './state';
+import { Transformer } from './transformer';
+
+export { StateAction };
+
+interface StateAction<A extends StateObject = StateObject, T = any, P = unknown> {
+    readonly id: UUID,
+    readonly definition: StateAction.Definition<A, T, P>
+}
+
+namespace StateAction {
+    export type Id = string & { '@type': 'transformer-id' }
+    export type Params<T extends StateAction<any, any, any>> = T extends StateAction<any, any, infer P> ? P : unknown;
+    export type ReType<T extends StateAction<any, any, any>> = T extends StateAction<any, infer T, any> ? T : unknown;
+    export type ControlsFor<Props> = { [P in keyof Props]?: PD.Any }
+
+    export interface ApplyParams<A extends StateObject = StateObject, P = unknown> {
+        cell: StateObjectCell,
+        a: A,
+        state: State,
+        params: P
+    }
+
+    export interface Definition<A extends StateObject = StateObject, T = any, P = unknown> {
+        readonly from: StateObject.Ctor[],
+        readonly display?: { readonly name: string, readonly description?: string },
+
+        /**
+         * Apply an action that modifies the State specified in Params.
+         */
+        apply(params: ApplyParams<A, P>, globalCtx: unknown): T | Task<T>,
+
+        readonly params?: Transformer<A, any, P>['definition']['params'],
+
+        /** Test if the transform can be applied to a given node */
+        isApplicable?(a: A, globalCtx: unknown): boolean
+    }
+
+    export function create<A extends StateObject, T, P>(definition: Definition<A, T, P>): StateAction<A, T, P> {
+        return { id: UUID.create(), definition };
+    }
+
+    export function fromTransformer<T extends Transformer>(transformer: T) {
+        const def = transformer.definition;
+        return create<Transformer.From<T>, void, Transformer.Params<T>>({
+            from: def.from,
+            display: def.display,
+            params: def.params as Transformer<Transformer.From<T>, any, Transformer.Params<T>>['definition']['params'],
+            apply({ cell, state, params }) {
+                const tree = state.build().to(cell.transform.ref).apply(transformer, params);
+                return state.update(tree);
+            }
+        })
+    }
+}

+ 0 - 0
src/mol-state/action/manager.ts


+ 6 - 14
src/mol-state/state.ts

@@ -13,6 +13,7 @@ import { RuntimeContext, Task } from 'mol-task';
 import { StateSelection } from './state/selection';
 import { RxEventHelper } from 'mol-util/rx-event-helper';
 import { StateTreeBuilder } from './tree/builder';
+import { StateAction } from './action';
 
 export { State }
 
@@ -80,6 +81,10 @@ class State {
         return StateSelection.select(selector(StateSelection.Generators), this)
     }
 
+    apply(action: StateAction, ref: Transform.Ref) {
+        
+    }
+
     update(tree: StateTree | StateTreeBuilder): Task<void> {
         // TODO: support cell state
         const _tree = StateTreeBuilder.is(tree) ? tree.getTree() : tree;
@@ -254,19 +259,6 @@ function doError(ctx: UpdateContext, ref: Ref, errorText: string) {
     }
 }
 
-function findAncestor(tree: StateTree, cells: State.Cells, root: Ref, types: { type: StateObject.Type }[]): StateObjectCell | undefined {
-    let current = tree.nodes.get(root)!;
-    while (true) {
-        current = tree.nodes.get(current.parent)!;
-        const cell = cells.get(current.ref)!;
-        if (!cell.obj) return void 0;
-        for (const t of types) if (cell.obj.type === t.type) return cells.get(current.ref)!;
-        if (current.ref === Transform.RootRef) {
-            return void 0;
-        }
-    }
-}
-
 async function updateSubtree(ctx: UpdateContext, root: Ref) {
     setCellStatus(ctx, root, 'processing');
 
@@ -296,7 +288,7 @@ async function updateSubtree(ctx: UpdateContext, root: Ref) {
 async function updateNode(ctx: UpdateContext, currentRef: Ref) {
     const { oldTree, tree } = ctx;
     const transform = tree.nodes.get(currentRef);
-    const parentCell = findAncestor(tree, ctx.cells, currentRef, transform.transformer.definition.from);
+    const parentCell = StateSelection.findAncestorOfType(tree, ctx.cells, currentRef, transform.transformer.definition.from);
 
     if (!parentCell) {
         throw new Error(`No suitable parent found for '${currentRef}'`);

+ 9 - 6
src/mol-state/state/selection.ts

@@ -123,6 +123,7 @@ namespace StateSelection {
             const set = new Set<string>();
             const ret: StateObjectCell[] = [];
             for (const n of q(state)) {
+                if (!n) continue;
                 if (!set.has(n.transform.ref)) {
                     set.add(n.transform.ref);
                     ret.push(n);
@@ -169,22 +170,24 @@ namespace StateSelection {
     export function ofType(b: Selector, t: StateObject.Type) { return filter(b, n => n.obj ? n.obj.type === t : false); }
 
     registerModifier('ancestorOfType', ancestorOfType);
-    export function ancestorOfType(b: Selector, types: StateObject.Ctor[]) { return unique(mapEntity(b, (n, s) => findAncestorOfType(s, n.transform.ref, types))); }
+    export function ancestorOfType(b: Selector, types: StateObject.Ctor[]) { return unique(mapEntity(b, (n, s) => findAncestorOfType(s.tree, s.cells, n.transform.ref, types))); }
 
     registerModifier('parent', parent);
     export function parent(b: Selector) { return unique(mapEntity(b, (n, s) => s.cells.get(s.tree.nodes.get(n.transform.ref)!.parent))); }
 
-    function findAncestorOfType({ tree, cells }: State, root: string, types: StateObject.Ctor[]): StateObjectCell | undefined {
+    export function findAncestorOfType(tree: StateTree, cells: State.Cells, root: Transform.Ref, types: StateObject.Ctor[]): StateObjectCell | undefined {
         let current = tree.nodes.get(root)!, len = types.length;
         while (true) {
             current = tree.nodes.get(current.parent)!;
-            if (current.ref === Transform.RootRef) {
-                return cells.get(Transform.RootRef);
-            }
-            const obj = cells.get(current.ref)!.obj!;
+            const cell = cells.get(current.ref)!;
+            if (!cell.obj) return void 0;
+            const obj = cell.obj;
             for (let i = 0; i < len; i++) {
                 if (obj.type === types[i].type) return cells.get(current.ref);
             }
+            if (current.ref === Transform.RootRef) {
+                return void 0;
+            }
         }
     }
 }

+ 5 - 1
src/mol-state/transformer.ts

@@ -8,9 +8,11 @@ import { Task } from 'mol-task';
 import { StateObject } from './object';
 import { Transform } from './transform';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
+import { StateAction } from './action';
 
 export interface Transformer<A extends StateObject = StateObject, B extends StateObject = StateObject, P = unknown> {
     apply(parent: Transform.Ref, params?: P, props?: Partial<Transform.Options>): Transform<A, B, P>,
+    toAction(): StateAction<A, void, P>,
     readonly namespace: string,
     readonly id: Transformer.Id,
     readonly definition: Transformer.Definition<A, B, P>
@@ -19,6 +21,7 @@ export interface Transformer<A extends StateObject = StateObject, B extends Stat
 export namespace Transformer {
     export type Id = string & { '@type': 'transformer-id' }
     export type Params<T extends Transformer<any, any, any>> = T extends Transformer<any, any, infer P> ? P : unknown;
+    export type From<T extends Transformer<any, any, any>> = T extends Transformer<infer A, any, any> ? A : unknown;
     export type To<T extends Transformer<any, any, any>> = T extends Transformer<any, infer B, any> ? B : unknown;
     export type ControlsFor<Props> = { [P in keyof Props]?: PD.Any }
 
@@ -114,7 +117,8 @@ export namespace Transformer {
         }
 
         const t: Transformer<A, B, P> = {
-            apply(parent, params, props) { return Transform.create<A, B, P>(parent, t as any, params, props); },
+            apply(parent, params, props) { return Transform.create<A, B, P>(parent, t, params, props); },
+            toAction() { return StateAction.fromTransformer(t); },
             namespace,
             id,
             definition