فهرست منبع

[wip] mol-state: dependent transforms

David Sehnal 5 سال پیش
والد
کامیت
a5f73b5c20
4فایلهای تغییر یافته به همراه103 افزوده شده و 10 حذف شده
  1. 6 1
      src/mol-state/object.ts
  2. 70 8
      src/mol-state/state.ts
  3. 11 1
      src/mol-state/transform.ts
  4. 16 0
      src/mol-util/array.ts

+ 6 - 1
src/mol-state/object.ts

@@ -69,7 +69,12 @@ interface StateObjectCell<T extends StateObject = StateObject, F extends StateTr
     params: {
         definition: ParamDefinition.Params,
         values: any
-    } | undefined;
+    } | undefined,
+
+    links: {
+        from: StateObjectCell[],
+        to: StateObjectCell[]
+    },
 
     errorText?: string,
     obj?: T,

+ 70 - 8
src/mol-state/state.ts

@@ -21,6 +21,8 @@ import { ParamDefinition } from '../mol-util/param-definition';
 import { StateTreeSpine } from './tree/spine';
 import { AsyncQueue } from '../mol-util/async-queue';
 import { isProductionMode } from '../mol-util/debug'
+import { arraySetAdd, arraySetRemove } from '../mol-util/array';
+import { UniqueArray } from '../mol-data/generic';
 
 export { State }
 
@@ -228,6 +230,7 @@ class State {
                 definition: {},
                 values: {}
             },
+            links: { from: [], to: [] },
             cache: { }
         });
 
@@ -322,7 +325,9 @@ async function update(ctx: UpdateContext) {
         }
 
         for (const d of deletes) {
-            const obj = ctx.cells.has(d) ? ctx.cells.get(d)!.obj : void 0;
+            const cell = ctx.cells.get(d);
+            if (cell) unlinkCell(cell);
+            const obj = cell && cell.obj;
             ctx.cells.delete(d);
             deletedObjects.push(obj);
         }
@@ -333,10 +338,10 @@ async function update(ctx: UpdateContext) {
 
     // Init empty cells where not present
     // this is done in "pre order", meaning that "parents" will be created 1st.
-    const addedCells = initCells(ctx, roots);
+    const init = initCells(ctx, roots);
 
     // Notify additions of new cells.
-    for (const cell of addedCells) {
+    for (const cell of init.added) {
         ctx.parent.events.cell.created.next({ state: ctx.parent, ref: cell.transform.ref, cell });
     }
 
@@ -349,6 +354,12 @@ async function update(ctx: UpdateContext) {
 
     if (deletedObjects.length) deletedObjects = [];
 
+    if (init.dependent) {
+        for (const cell of init.dependent) {
+            roots.push(cell.transform.ref);
+        }
+    }
+
     // Set status of cells that will be updated to 'pending'.
     initCellStatus(ctx, roots);
 
@@ -450,8 +461,17 @@ function initCellStatus(ctx: UpdateContext, roots: Ref[]) {
     }
 }
 
-type InitCellsCtx = { ctx: UpdateContext, added: StateObjectCell[] }
-function initCellsVisitor(transform: StateTransform, _: any, { ctx, added }: InitCellsCtx) {
+function unlinkCell(cell: StateObjectCell) {
+    for (const other of cell.links.to) {
+        arraySetRemove(other.links.from, cell);
+    }
+}
+
+type InitCellsCtx = { ctx: UpdateContext, visited: Set<Ref>, added: StateObjectCell[] }
+
+function addCellsVisitor(transform: StateTransform, _: any, { ctx, added, visited }: InitCellsCtx) {
+    visited.add(transform.ref);
+
     if (ctx.cells.has(transform.ref)) {
         return;
     }
@@ -464,18 +484,60 @@ function initCellsVisitor(transform: StateTransform, _: any, { ctx, added }: Ini
         state: { ...transform.state },
         errorText: void 0,
         params: void 0,
+        links: { from: [], to: [] },
         cache: void 0
     };
+
     ctx.cells.set(transform.ref, cell);
     added.push(cell);
 }
 
+// type LinkCellsCtx = { ctx: UpdateContext, visited: Set<Ref>, dependent: UniqueArray<Ref, StateObjectCell> }
+
+function linkCells(target: StateObjectCell, ctx: UpdateContext) {
+    if (!target.transform.dependsOn) return;
+
+    for (const ref of target.transform.dependsOn) {
+        const t = ctx.tree.transforms.get(ref);
+        if (!t) {
+            throw new Error(`Cannot depend on a non-existent transform.`);
+        }
+
+        const cell = ctx.cells.get(ref)!;
+        arraySetAdd(target.links.to, cell);
+        arraySetAdd(cell.links.from, target);
+    }
+}
+
 function initCells(ctx: UpdateContext, roots: Ref[]) {
-    const initCtx: InitCellsCtx = { ctx, added: [] };
+    const initCtx: InitCellsCtx = { ctx, visited: new Set(), added: [] };
+
+    // Add new cells
     for (const root of roots) {
-        StateTree.doPreOrder(ctx.tree, ctx.tree.transforms.get(root), initCtx, initCellsVisitor);
+        StateTree.doPreOrder(ctx.tree, ctx.tree.transforms.get(root), initCtx, addCellsVisitor);
+    }
+
+    // Update links for newly added cells
+    for (const cell of initCtx.added) {
+        linkCells(cell, ctx);
     }
-    return initCtx.added;
+
+    let dependent: UniqueArray<Ref, StateObjectCell>;
+
+    // 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;
+
+            if (!dependent) dependent = UniqueArray.create();
+            UniqueArray.add(dependent, on.transform.ref, on);
+        }
+    });
+
+    // TODO: check if dependent cells are all "proper roots"
+
+    return { added: initCtx.added, dependent: dependent! ? dependent!.array : void 0 };
 }
 
 function findNewCurrent(tree: StateTree, start: Ref, deletes: Ref[], cells: Map<Ref, StateObjectCell>) {

+ 11 - 1
src/mol-state/transform.ts

@@ -15,6 +15,11 @@ interface Transform<T extends StateTransformer = StateTransformer> {
     readonly state: Transform.State,
     readonly tags?: string[],
     readonly ref: Transform.Ref,
+    /**
+     * Sibling-like dependency
+     * Do NOT make a cell dependent on its ancestor.
+     */
+    readonly dependsOn?: Transform.Ref[],
     readonly params?: StateTransformer.Params<T>,
     readonly version: string
 }
@@ -85,7 +90,8 @@ namespace Transform {
     export interface Options {
         ref?: string,
         tags?: string | string[],
-        state?: State
+        state?: State,
+        dependsOn?: Ref[]
     }
 
     export function create<T extends StateTransformer>(parent: Ref, transformer: T, params?: StateTransformer.Params<T>, options?: Options): Transform<T> {
@@ -100,6 +106,7 @@ namespace Transform {
             state: (options && options.state) || { },
             tags,
             ref,
+            dependsOn: options && options.dependsOn,
             params,
             version: UUID.create22()
         }
@@ -134,6 +141,7 @@ namespace Transform {
         state?: State,
         tags?: string[],
         ref: string,
+        dependsOn?: string[]
         version: string
     }
 
@@ -156,6 +164,7 @@ namespace Transform {
             state,
             tags: t.tags,
             ref: t.ref,
+            dependsOn: t.dependsOn,
             version: t.version
         };
     }
@@ -172,6 +181,7 @@ namespace Transform {
             state: t.state || { },
             tags: t.tags,
             ref: t.ref as Ref,
+            dependsOn: t.dependsOn,
             version: t.version
         };
     }

+ 16 - 0
src/mol-util/array.ts

@@ -84,4 +84,20 @@ export function arrayRemoveInPlace<T>(xs: T[], x: T) {
     }
     xs.pop();
     return true;
+}
+
+export function arraySetAdd<T>(xs: T[], x: T) {
+    if (xs.indexOf(x) >= 0) return false;
+    xs.push(x);
+    return true;
+}
+
+export function arraySetRemove<T>(xs: T[], x: T) {
+    const idx = xs.indexOf(x);
+    if (idx < 0) return false;
+    for (let i = idx, _i = xs.length - 1; i < _i; i++) {
+        xs[i] = xs[i + 1];
+    }
+    xs.pop();
+    return true;
 }