David Sehnal 6 роки тому
батько
коміт
cb68970783

+ 49 - 0
src/mol-state/context.ts

@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { Subject } from 'rxjs'
+import { StateObject } from './object';
+import { Task } from 'mol-task';
+import { Transform } from './tree/transform';
+
+interface StateContext {
+    events: {
+        object: {
+            stateChanged: Subject<{ ref: Transform.Ref }>,
+            updated: Subject<{ ref: Transform.Ref }>,
+            replaced: Subject<{ ref: Transform.Ref, old?: StateObject }>,
+            created: Subject<{ ref: Transform.Ref }>,
+            removed: Subject<{ ref: Transform.Ref }>,
+        },
+        warn: Subject<string>
+    },
+    globalContext: unknown,
+    runTask<T>(task: T | Task<T>): T | Promise<T>
+}
+
+namespace StateContext {
+    export function create(globalContext?: unknown/* task?: { observer?: Progress.Observer, updateRateMs?: number } */): StateContext {
+        return {
+            events: {
+                object: {
+                    stateChanged: new Subject(),
+                    updated: new Subject(),
+                    replaced: new Subject(),
+                    created: new Subject(),
+                    removed: new Subject()
+                },
+                warn: new Subject()
+            },
+            globalContext,
+            runTask<T>(t: T | Task<T>) {
+                if (typeof (t as any).run === 'function') return (t as Task<T>).run();
+                return t as T;
+            }
+        }
+    }
+}
+
+export { StateContext }

+ 0 - 9
src/mol-state/context/context.ts

@@ -1,9 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-export interface StateContext {
-
-}

+ 0 - 9
src/mol-state/event/dispatcher.ts

@@ -1,9 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-export interface EventDispatcher {
-    // TODO
-}

+ 0 - 9
src/mol-state/event/event.ts

@@ -1,9 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-export interface EventDispatcher {
-    // TODO
-}

+ 9 - 1
src/mol-state/index.ts

@@ -2,4 +2,12 @@
  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
- */
+ */
+
+export * from './object'
+export * from './state'
+export * from './transformer'
+export * from './tree'
+export * from './context'
+export * from './tree/transform'
+export * from './tree/selection'

+ 2 - 7
src/mol-state/object.ts

@@ -16,12 +16,6 @@ export interface StateObject<P = unknown, D = unknown> {
 }
 
 export namespace StateObject {
-    // export type TypeOf<T>
-    //     = T extends StateObject<infer X> ? [X]
-    //     : T extends [StateObject<infer X>] ? [X]
-    //     : T extends [StateObject<infer X>, StateObject<infer Y>] ? [X, Y]
-    //     : unknown[];
-
     export enum StateType {
         // The object has been successfully created
         Ok,
@@ -53,8 +47,9 @@ export namespace StateObject {
     }
 
     export interface Wrapped {
-        obj: StateObject,
         state: StateType,
+        errorText?: string,
+        obj?: StateObject,
         version: string
     }
 }

+ 133 - 59
src/mol-state/state.ts

@@ -5,31 +5,34 @@
  */
 
 import { StateObject } from './object';
-import { TransformTree } from './tree/tree';
+import { StateTree } from './tree';
 import { Transform } from './tree/transform';
 import { Map as ImmutableMap } from 'immutable';
 // import { StateContext } from './context/context';
 import { ImmutableTree } from './util/immutable-tree';
 import { Transformer } from './transformer';
-import { Task } from 'mol-task';
+import { StateContext } from './context';
+import { UUID } from 'mol-util';
 
 export interface State<ObjectProps = unknown> {
     definition: State.Definition<ObjectProps>,
-    objects: State.Objects
+    objects: State.Objects,
+    context: StateContext
 }
 
 export namespace State {
-    export type ObjectProps<P> = ImmutableMap<Transform.Ref, P>
-    export type Objects = Map<Transform.Ref, StateObject.Wrapped>
+    export type Ref = Transform.Ref
+    export type ObjectProps<P = unknown> = ImmutableMap<Ref, P>
+    export type Objects = Map<Ref, StateObject.Wrapped>
 
     export interface Definition<P = unknown> {
-        tree: TransformTree,
+        tree: StateTree,
         // things like object visibility
         props: ObjectProps<P>
     }
 
-    export function create(): State {
-        const tree = TransformTree.create();
+    export function create(params?: { globalContext?: unknown }): State {
+        const tree = StateTree.create();
         const objects: Objects = new Map();
         const root = tree.getValue(tree.rootRef)!;
 
@@ -40,44 +43,60 @@ export namespace State {
                 tree,
                 props: ImmutableMap()
             },
-            objects
+            objects,
+            context: StateContext.create(params && params.globalContext)
         };
     }
 
-    export async function update<P>(state: State<P>, tree: TransformTree, props?: ObjectProps<P>): Promise<State<P>> {
+    export async function update<P>(state: State<P>, tree: StateTree): Promise<State<P>> {
+        const ctx: UpdateContext = {
+            stateCtx: state.context,
+            old: state.definition,
+            tree: tree,
+            props: state.definition.props.asMutable(),
+            objects: state.objects
+        };
+
         const roots = findUpdateRoots(state.objects, tree);
-        const deletes = findDeletes(state.objects, tree);
+        const deletes = findDeletes(ctx);
         for (const d of deletes) {
             state.objects.delete(d);
         }
 
-        console.log('roots', roots);
+        initObjectState(ctx, roots);
+
         for (const root of roots) {
-            await updateSubtree(state.definition.tree, tree, state.objects, root);
+            await updateSubtree(ctx, root);
         }
 
         return {
-            definition: { tree, props: props || state.definition.props },
-            objects: state.objects
+            definition: { tree, props: ctx.props.asImmutable() as ObjectProps<P> },
+            objects: state.objects,
+            context: state.context
         };
     }
 
-    function findUpdateRoots(objects: Objects, tree: TransformTree) {
-        console.log(tree);
+    interface UpdateContext {
+        stateCtx: StateContext,
+        old: Definition,
+        tree: StateTree,
+        props: ObjectProps,
+        objects: Objects
+    }
+
+    function findUpdateRoots(objects: Objects, tree: StateTree) {
         const findState = {
-            roots: [] as Transform.Ref[],
+            roots: [] as Ref[],
             objects
         };
 
         ImmutableTree.doPreOrder(tree, tree.nodes.get(tree.rootRef)!, findState, (n, _, s) => {
             if (!s.objects.has(n.ref)) {
-                console.log('missing', n.ref);
                 s.roots.push(n.ref);
                 return false;
             }
             const o = s.objects.get(n.ref)!;
             if (o.version !== n.value.version) {
-                console.log('diff version', n.ref, n.value.version, o.version);
                 s.roots.push(n.ref);
                 return false;
             }
@@ -88,75 +107,130 @@ export namespace State {
         return findState.roots;
     }
 
-    function findDeletes(objects: Objects, tree: TransformTree): Transform.Ref[] {
-        // TODO
-        return [];
+    function findDeletes(ctx: UpdateContext): Ref[] {
+        // TODO: do this in some sort of "tree order"?
+        const deletes: Ref[] = [];
+        const keys = ctx.objects.keys();
+        while (true) {
+            const key = keys.next();
+            if (key.done) break;
+            if (!ctx.tree.nodes.has(key.value)) deletes.push(key.value);
+        }
+        return deletes;
+    }
+
+    function setObjectState(ctx: UpdateContext, ref: Ref, state: StateObject.StateType, errorText?: string) {
+        if (ctx.objects.has(ref)) {
+            const obj = ctx.objects.get(ref)!;
+            obj.state = state;
+            obj.errorText = errorText;
+        } else {
+            const obj = { state, version: UUID.create(), errorText };
+            ctx.objects.set(ref, obj);
+        }
+        ctx.stateCtx.events.object.stateChanged.next({ ref });
     }
 
-    function findParent(tree: TransformTree, objects: Objects, root: Transform.Ref, types: { type: StateObject.Type }[]): StateObject {
+    function _initVisitor(t: ImmutableTree.Node<Transform>, _: any, ctx: UpdateContext) {
+        setObjectState(ctx, t.ref, StateObject.StateType.Pending);
+    }
+    /** Return "resolve set" */
+    function initObjectState(ctx: UpdateContext, roots: Ref[]) {
+        for (const root of roots) {
+            ImmutableTree.doPreOrder(ctx.tree, ctx.tree.nodes.get(root), ctx, _initVisitor);
+        }
+    }
+
+    function doError(ctx: UpdateContext, ref: Ref, errorText: string) {
+        setObjectState(ctx, ref, StateObject.StateType.Error, errorText);
+        const wrap = ctx.objects.get(ref)!;
+        if (wrap.obj) {
+            ctx.stateCtx.events.object.removed.next({ ref });
+            wrap.obj = void 0;
+        }
+
+        const children = ctx.tree.nodes.get(ref)!.children.values();
+        while (true) {
+            const next = children.next();
+            if (next.done) return;
+            doError(ctx, next.value, 'Parent node contains error.');
+        }
+    }
+
+    function findParent(tree: StateTree, objects: Objects, root: Ref, types: { type: StateObject.Type }[]): StateObject {
         let current = tree.nodes.get(root)!;
-        console.log('finding', types.map(t => t.type.kind));
         while (true) {
             current = tree.nodes.get(current.parent)!;
-            if (current.ref === tree.rootRef) return objects.get(tree.rootRef)!.obj;
-            const obj = objects.get(current.ref)!.obj;
-            console.log('current', obj.type.kind);
-            for (const t of types) if (obj.type === t.type) return objects.get(current.ref)!.obj;
+            if (current.ref === tree.rootRef) {
+                return objects.get(tree.rootRef)!.obj!;
+            }
+            const obj = objects.get(current.ref)!.obj!;
+            for (const t of types) if (obj.type === t.type) return objects.get(current.ref)!.obj!;
         }
     }
 
-    async function updateSubtree(oldTree: TransformTree, tree: TransformTree, objects: Objects, root: Transform.Ref) {
-        await updateNode(oldTree, tree, objects, root);
-        const children = tree.nodes.get(root)!.children.values();
+    async function updateSubtree(ctx: UpdateContext, root: Ref) {
+        setObjectState(ctx, root, StateObject.StateType.Pending);
+
+        try {
+            const update = await updateNode(ctx, root);
+            setObjectState(ctx, root, StateObject.StateType.Ok);
+            if (update === 'created') {
+                ctx.stateCtx.events.object.created.next({ ref: root });
+            } else if (update === 'updated') {
+                ctx.stateCtx.events.object.updated.next({ ref: root });
+            }
+        } catch (e) {
+            doError(ctx, root, '' + e);
+            return;
+        }
+
+        const children = ctx.tree.nodes.get(root)!.children.values();
         while (true) {
             const next = children.next();
             if (next.done) return;
-            await updateSubtree(oldTree, tree, objects, next.value);
+            await updateSubtree(ctx, next.value);
         }
     }
 
-    async function updateNode(oldTree: TransformTree, tree: TransformTree, objects: Objects, currentRef: Transform.Ref) {
+    async function updateNode(ctx: UpdateContext, currentRef: Ref) {
+        const { old: { tree: oldTree }, tree, objects } = ctx;
         const transform = tree.getValue(currentRef)!;
         const parent = findParent(tree, objects, currentRef, transform.transformer.definition.from);
-        console.log('parent', parent ? parent.ref : 'undefined')
-        if (!oldTree.nodes.has(transform.ref) || !objects.has(transform.ref)) {
-            console.log('creating...', transform.transformer.id, oldTree.nodes.has(transform.ref), objects.has(transform.ref));
-            const obj = await createObject(transform.transformer, parent, transform.params);
-            obj.ref = transform.ref;
+        //console.log('parent', transform.transformer.id, transform.transformer.definition.from[0].type, parent ? parent.ref : 'undefined')
+        if (!oldTree.nodes.has(currentRef) || !objects.has(currentRef)) {
+            console.log('creating...', transform.transformer.id, oldTree.nodes.has(currentRef), objects.has(currentRef));
+            const obj = await createObject(ctx, transform.transformer, parent, transform.params);
+            obj.ref = currentRef;
             objects.set(currentRef, { obj, state: StateObject.StateType.Ok, version: transform.version });
+            return 'created';
         } else {
             console.log('updating...', transform.transformer.id);
-            const current = objects.get(transform.ref)!.obj;
-            const oldParams = oldTree.getValue(transform.ref)!.params;
-            switch (await updateObject(transform.transformer, parent, current, oldParams, transform.params)) {
+            const current = objects.get(currentRef)!;
+            const oldParams = oldTree.getValue(currentRef)!.params;
+            switch (await updateObject(ctx, transform.transformer, parent, current.obj!, oldParams, transform.params)) {
                 case Transformer.UpdateResult.Recreate: {
-                    const obj = await createObject(transform.transformer, parent, transform.params);
-                    obj.ref = transform.ref;
+                    const obj = await createObject(ctx, transform.transformer, parent, transform.params);
+                    obj.ref = currentRef;
                     objects.set(currentRef, { obj, state: StateObject.StateType.Ok, version: transform.version });
-                    break;
-                }
-                case Transformer.UpdateResult.Updated: {
-                    const obj = objects.get(currentRef)!;
-                    obj.version = transform.version;
-                    break;
+                    ctx.stateCtx.events.object.created.next({ ref: currentRef });
+                    return 'created';
                 }
+                case Transformer.UpdateResult.Updated:
+                    current.version = transform.version;
+                    return 'updated';
             }
         }
     }
 
-    async function runTask<A>(t: A | Task<A>): Promise<A> {
-        if ((t as any).run) return await (t as Task<A>).run();
-        return t as A;
-    }
-
-    function createObject(transformer: Transformer, a: StateObject, params: any) {
-        return runTask(transformer.definition.apply({ a, params }));
+    function createObject(ctx: UpdateContext, transformer: Transformer, a: StateObject, params: any) {
+        return ctx.stateCtx.runTask(transformer.definition.apply({ a, params, globalCtx: ctx.stateCtx.globalContext }));
     }
 
-    async function updateObject(transformer: Transformer, a: StateObject, b: StateObject, oldParams: any, newParams: any) {
+    async function updateObject(ctx: UpdateContext, transformer: Transformer, a: StateObject, b: StateObject, oldParams: any, newParams: any) {
         if (!transformer.definition.update) {
             return Transformer.UpdateResult.Recreate;
         }
-        return runTask(transformer.definition.update({ a, oldParams, b, newParams }));
+        return ctx.stateCtx.runTask(transformer.definition.update({ a, oldParams, b, newParams, globalCtx: ctx.stateCtx.globalContext }));
     }
 }

+ 8 - 7
src/mol-state/transformer.ts

@@ -6,7 +6,6 @@
 
 import { Task } from 'mol-task';
 import { StateObject } from './object';
-import { TransformContext } from './tree/context';
 import { Transform } from './tree/transform';
 
 export interface Transformer<A extends StateObject = StateObject, B extends StateObject = StateObject, P = unknown> {
@@ -24,14 +23,16 @@ export namespace Transformer {
 
     export interface ApplyParams<A extends StateObject = StateObject, P = unknown> {
         a: A,
-        params: P
+        params: P,
+        globalCtx: unknown
     }
 
     export interface UpdateParams<A extends StateObject = StateObject, B extends StateObject = StateObject, P = unknown> {
         a: A,
         b: B,
         oldParams: P,
-        newParams: P
+        newParams: P,
+        globalCtx: unknown
     }
 
     export enum UpdateResult { Unchanged, Updated, Recreate }
@@ -55,19 +56,19 @@ export namespace Transformer {
         update?(params: UpdateParams<A, B, P>): Task<UpdateResult> | UpdateResult,
 
         /** Check the parameters and return a list of errors if the are not valid. */
-        defaultParams?(a: A, context: TransformContext): P,
+        defaultParams?(a: A, globalCtx: unknown): P,
 
         /** Specify default control descriptors for the parameters */
-        defaultControls?(a: A, context: TransformContext): Transformer.ControlsFor<P>,
+        defaultControls?(a: A, globalCtx: unknown): Transformer.ControlsFor<P>,
 
         /** Check the parameters and return a list of errors if the are not valid. */
-        validateParams?(a: A, params: P, context: TransformContext): string[] | undefined,
+        validateParams?(a: A, params: P, globalCtx: unknown): string[] | undefined,
 
         /** Optional custom parameter equality. Use deep structural equal by default. */
         areParamsEqual?(oldParams: P, newParams: P): boolean,
 
         /** Test if the transform can be applied to a given node */
-        isApplicable?(a: A, context: TransformContext): boolean,
+        isApplicable?(a: A, globalCtx: unknown): boolean,
 
         /** By default, returns true */
         isSerializable?(params: P): { isSerializable: true } | { isSerializable: false; reason: string },

+ 0 - 7
src/mol-state/transformer/controller.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: relactive transformer params controller

+ 0 - 7
src/mol-state/transformer/manager.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: index for registered transformers

+ 79 - 0
src/mol-state/tree.ts

@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { Transform } from './tree/transform';
+import { ImmutableTree } from './util/immutable-tree';
+import { Transformer } from './transformer';
+import { StateObject } from './object';
+
+interface StateTree extends ImmutableTree<Transform> { }
+
+namespace StateTree {
+    export interface Transient extends ImmutableTree.Transient<Transform> { }
+
+    function _getRef(t: Transform) { return t.ref; }
+
+    export function create() {
+        return ImmutableTree.create<Transform>(Transform.createRoot('<:root:>'), _getRef);
+    }
+
+    export function updateParams<T extends Transformer = Transformer>(tree: StateTree, ref: Transform.Ref, params: Transformer.Params<T>): StateTree {
+        const t = tree.nodes.get(ref)!.value;
+        const newTransform = Transform.updateParams(t, params);
+        const newTree = ImmutableTree.asTransient(tree);
+        newTree.setValue(ref, newTransform);
+        return newTree.asImmutable();
+    }
+
+    export function toJSON(tree: StateTree) {
+        return ImmutableTree.toJSON(tree, Transform.toJSON);
+    }
+
+    export function fromJSON(data: any): StateTree {
+        return ImmutableTree.fromJSON(data, _getRef, Transform.fromJSON);
+    }
+
+    export interface Builder {
+        getTree(): StateTree
+    }
+
+    export function build(tree: StateTree) {
+        return new Builder.Root(tree);
+    }
+
+    export namespace Builder {
+        interface State {
+            tree: StateTree.Transient
+        }
+
+        export class Root implements Builder {
+            private state: State;
+            to<A extends StateObject>(ref: Transform.Ref) { return new To<A>(this.state, ref); }
+            toRoot<A extends StateObject>() { return new To<A>(this.state, this.state.tree.rootRef as any); }
+            delete(ref: Transform.Ref) { this.state.tree.remove(ref); return this; }
+            getTree(): StateTree { return this.state.tree.asImmutable(); }
+            constructor(tree: StateTree) { this.state = { tree: ImmutableTree.asTransient(tree) } }
+        }
+
+        export class To<A extends StateObject> implements Builder {
+            apply<T extends Transformer<A, any, any>>(tr: T, params?: Transformer.Params<T>, props?: Partial<Transform.Props>): To<Transformer.To<T>> {
+                const t = tr.apply(params, props);
+                this.state.tree.add(this.ref, t);
+                return new To(this.state, t.ref);
+            }
+
+            getTree(): StateTree { return this.state.tree.asImmutable(); }
+
+            constructor(private state: State, private ref: Transform.Ref) {
+                if (!this.state.tree.nodes.has(ref)) {
+                    throw new Error(`Could not find node '${ref}'.`);
+                }
+            }
+        }
+    }
+}
+
+export { StateTree }

+ 0 - 7
src/mol-state/tree/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: tree actions: Add, Update, Delete; action queue

+ 0 - 48
src/mol-state/tree/builder.ts

@@ -1,48 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { ImmutableTree } from '../util/immutable-tree';
-import { TransformTree } from './tree';
-import { StateObject } from '../object';
-import { Transform } from './transform';
-import { Transformer } from '../transformer';
-
-export interface StateTreeBuilder {
-    getTree(): TransformTree
-}
-
-export namespace StateTreeBuilder {
-    interface State {
-        tree: TransformTree.Transient
-    }
-
-    export function create(tree: TransformTree) {
-        return new Root(tree);
-    }
-
-    export class Root implements StateTreeBuilder {
-        private state: State;
-        //to<A extends StateObject>(ref: Transform.Ref) { return new To<A>(this.state, ref); }
-        to<A extends StateObject>(ref: Transform.Ref) { return new To<A>(this.state, ref); }
-        toRoot<A extends StateObject>() { return new To<A>(this.state, this.state.tree.rootRef as any); }
-        getTree(): TransformTree { return this.state.tree.asImmutable(); }
-        constructor(tree: TransformTree) { this.state = { tree: ImmutableTree.asTransient(tree) } }
-    }
-
-    export class To<A extends StateObject> implements StateTreeBuilder {
-        apply<T extends Transformer<A, any, any>>(tr: T, params?: Transformer.Params<T>, props?: Partial<Transform.Props>): To<Transformer.To<T>> {
-            const t = tr.apply(params, props);
-            this.state.tree.add(this.ref, t);
-            return new To(this.state, t.ref);
-        }
-
-        getTree(): TransformTree { return this.state.tree.asImmutable(); }
-
-        constructor(private state: State, private ref: Transform.Ref) {
-
-        }
-    }
-}

+ 0 - 15
src/mol-state/tree/context.ts

@@ -1,15 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { EventDispatcher } from '../event/event';
-
-export interface TransformContext {
-    /** An event dispatcher for executing child tasks. */
-    dispatcher: EventDispatcher,
-
-    globalContext: any
-    // tree: ModelTree
-}

+ 5 - 1
src/mol-state/tree/selection.ts

@@ -4,4 +4,8 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-// TODO
+namespace StateTreeSelection {
+
+}
+
+export { StateTreeSelection }

+ 8 - 4
src/mol-state/tree/transform.ts

@@ -12,7 +12,8 @@ export interface Transform<A extends StateObject = StateObject, B extends StateO
     readonly transformer: Transformer<A, B, P>,
     readonly params: P,
     readonly ref: Transform.Ref,
-    readonly version: string
+    readonly version: string,
+    readonly defaultProps?: unknown
 }
 
 export namespace Transform {
@@ -49,7 +50,8 @@ export namespace Transform {
         transformer: string,
         params: any,
         ref: string,
-        version: string
+        version: string,
+        defaultProps?: unknown
     }
 
     function _id(x: any) { return x; }
@@ -61,7 +63,8 @@ export namespace Transform {
             transformer: t.transformer.id,
             params: pToJson(t.params),
             ref: t.ref,
-            version: t.version
+            version: t.version,
+            defaultProps: t.defaultProps
         };
     }
 
@@ -74,7 +77,8 @@ export namespace Transform {
             transformer,
             params: pFromJson(t.params),
             ref: t.ref,
-            version: t.version
+            version: t.version,
+            defaultProps: t.defaultProps
         };
     }
 }

+ 0 - 37
src/mol-state/tree/tree.ts

@@ -1,37 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { Transform } from './transform';
-import { ImmutableTree } from '../util/immutable-tree';
-import { Transformer } from '../transformer';
-
-export interface TransformTree extends ImmutableTree<Transform> { }
-
-export namespace TransformTree {
-    export interface Transient extends ImmutableTree.Transient<Transform> { }
-
-    function _getRef(t: Transform) { return t.ref; }
-
-    export function create() {
-        return ImmutableTree.create<Transform>(Transform.createRoot('<:root:>'), _getRef);
-    }
-
-    export function updateParams<T extends Transformer = Transformer>(tree: TransformTree, ref: Transform.Ref, params: Transformer.Params<T>): TransformTree {
-        const t = tree.nodes.get(ref)!.value;
-        const newTransform = Transform.updateParams(t, params);
-        const newTree = ImmutableTree.asTransient(tree);
-        newTree.setValue(ref, newTransform);
-        return newTree.asImmutable();
-    }
-
-    export function toJSON(tree: TransformTree) {
-        return ImmutableTree.toJSON(tree, Transform.toJSON);
-    }
-
-    export function fromJSON(data: any): TransformTree {
-        return ImmutableTree.fromJSON(data, _getRef, Transform.fromJSON);
-    }
-}

+ 3 - 8
src/mol-state/util/immutable-tree.ts

@@ -62,7 +62,7 @@ export namespace ImmutableTree {
     type VisitorCtx = { nodes: Ns, state: any, f: (node: N, nodes: Ns, state: any) => boolean | undefined | void };
 
     function _postOrderFunc(this: VisitorCtx, c: ImmutableTree.Ref | undefined) { _doPostOrder(this, this.nodes.get(c!)!); }
-    function _doPostOrder<T, S>(ctx: VisitorCtx, root: N) {
+    function _doPostOrder(ctx: VisitorCtx, root: N) {
         if (root.children.size) {
             root.children.forEach(_postOrderFunc, ctx);
         }
@@ -79,7 +79,7 @@ export namespace ImmutableTree {
     }
 
     function _preOrderFunc(this: VisitorCtx, c: ImmutableTree.Ref | undefined) { _doPreOrder(this, this.nodes.get(c!)!); }
-    function _doPreOrder<T, S>(ctx: VisitorCtx, root: N) {
+    function _doPreOrder(ctx: VisitorCtx, root: N) {
         const ret = ctx.f(root, ctx.nodes, ctx.state);
         if (typeof ret === 'boolean' && !ret) return;
         if (root.children.size) {
@@ -225,12 +225,7 @@ export namespace ImmutableTree {
             const parent = nodes.get(node.parent)!;
             const children = mutate(parent.ref).children;
             const st = subtreePostOrder(this, node);
-            if (parent.ref === node.ref) {
-                nodes.clear();
-                mutations.clear();
-                return st;
-            }
-            children.delete(ref);
+            if (ref !== this.rootRef) children.delete(ref);
             for (const n of st) {
                 nodes.delete(n.value.ref);
                 mutations.delete(n.value.ref);

+ 7 - 11
src/perf-tests/state.ts

@@ -1,10 +1,6 @@
-import { Transformer } from 'mol-state/transformer';
-import { StateObject } from 'mol-state/object';
+import { State, StateObject, StateTree, Transformer } from 'mol-state';
 import { Task } from 'mol-task';
-import { TransformTree } from 'mol-state/tree/tree';
-import { StateTreeBuilder } from 'mol-state/tree/builder';
-import { State } from 'mol-state/state';
-import * as util from 'util'
+import * as util from 'util';
 
 export type TypeClass = 'root' | 'shape' | 'prop'
 export interface ObjProps { label: string }
@@ -72,7 +68,7 @@ export async function testState() {
     const state = State.create();
 
     const tree = state.definition.tree;
-    const builder = StateTreeBuilder.create(tree)
+    const builder = StateTree.build(tree);
     builder.toRoot<Root>()
         .apply(CreateSquare, { a: 10 }, { ref: 'square' })
         .apply(CaclArea);
@@ -80,7 +76,7 @@ export async function testState() {
 
     printTTree(tree1);
 
-    const tree2 = TransformTree.updateParams<typeof CreateSquare>(tree1, 'square', { a: 15 });
+    const tree2 = StateTree.updateParams<typeof CreateSquare>(tree1, 'square', { a: 15 });
     printTTree(tree1);
     printTTree(tree2);
 
@@ -89,11 +85,11 @@ export async function testState() {
     console.log(util.inspect(state1.objects, true, 3, true));
 
     console.log('----------------');
-    const jsonString = JSON.stringify(TransformTree.toJSON(tree2), null, 2);
+    const jsonString = JSON.stringify(StateTree.toJSON(tree2), null, 2);
     const jsonData = JSON.parse(jsonString);
     printTTree(tree2);
     console.log(jsonString);
-    const treeFromJson = TransformTree.fromJSON(jsonData);
+    const treeFromJson = StateTree.fromJSON(jsonData);
     printTTree(treeFromJson);
 
     console.log('----------------');
@@ -106,7 +102,7 @@ testState();
 
 //test();
 
-export function printTTree(tree: TransformTree) {
+export function printTTree(tree: StateTree) {
     let lines: string[] = [];
     function print(offset: string, ref: any) {
         const t = tree.nodes.get(ref)!;