From a74c8f1dfa0dedc8ac9d0f2d381db5ee413b8e3b Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Thu, 8 Nov 2018 20:27:02 +0100 Subject: [PATCH] mol-state: wip --- src/mol-plugin/state/base.ts | 10 +++--- src/mol-plugin/ui/controls.tsx | 2 +- src/mol-plugin/ui/plugin.tsx | 2 +- src/mol-plugin/ui/state-tree.tsx | 11 ++++--- src/mol-state/object.ts | 34 +++++++++----------- src/mol-state/selection.ts | 54 ++++++++++++++++---------------- src/mol-state/state.ts | 24 +++++++------- src/perf-tests/state.ts | 6 ++-- 8 files changed, 71 insertions(+), 72 deletions(-) diff --git a/src/mol-plugin/state/base.ts b/src/mol-plugin/state/base.ts index 05e2f6278..c3357949d 100644 --- a/src/mol-plugin/state/base.ts +++ b/src/mol-plugin/state/base.ts @@ -11,18 +11,20 @@ import { PluginBehavior } from 'mol-plugin/behavior/behavior'; export type TypeClass = 'root' | 'data' | 'prop' export namespace PluginStateObject { + export type Any = StateObject<Props, any, TypeInfo> + export type TypeClass = 'Root' | 'Group' | 'Data' | 'Object' | 'Representation3D' | 'Behavior' export interface TypeInfo { name: string, shortName: string, description: string, typeClass: TypeClass } export interface Props { label: string, description?: string } export const Create = StateObject.factory<TypeInfo, Props>(); - export function isRepresentation3D(o?: StateObject): o is StateObject<Props, Representation.Any> { - return !!o && (o.type.info as TypeInfo).typeClass === 'Representation3D'; + export function isRepresentation3D(o?: Any): o is StateObject<Props, Representation.Any, TypeInfo> { + return !!o && o.type.typeClass === 'Representation3D'; } - export function isBehavior(o?: StateObject): o is StateObject<Props, PluginBehavior> { - return !!o && (o.type.info as TypeInfo).typeClass === 'Behavior'; + export function isBehavior(o?: Any): o is StateObject<Props, PluginBehavior, TypeInfo> { + return !!o && o.type.typeClass === 'Behavior'; } export function CreateRepresentation3D<T extends Representation.Any>(type: { name: string, shortName: string, description: string }) { diff --git a/src/mol-plugin/ui/controls.tsx b/src/mol-plugin/ui/controls.tsx index ec6155d9f..71723f5b7 100644 --- a/src/mol-plugin/ui/controls.tsx +++ b/src/mol-plugin/ui/controls.tsx @@ -44,7 +44,7 @@ export class Controls extends React.Component<{ plugin: PluginContext }, { id: s export class _test_CreateTransform extends React.Component<{ plugin: PluginContext, nodeRef: Transform.Ref, transformer: Transformer }, { params: any }> { private getObj() { - const obj = this.props.plugin.state.data.objects.get(this.props.nodeRef)!; + const obj = this.props.plugin.state.data.cells.get(this.props.nodeRef)!; return obj; } diff --git a/src/mol-plugin/ui/plugin.tsx b/src/mol-plugin/ui/plugin.tsx index 111e510b9..520b55069 100644 --- a/src/mol-plugin/ui/plugin.tsx +++ b/src/mol-plugin/ui/plugin.tsx @@ -42,7 +42,7 @@ export class _test_CurrentObject extends React.Component<{ plugin: PluginContext render() { const ref = this.props.plugin.behaviors.state.data.currentObject.value.ref; // const n = this.props.plugin.state.data.tree.nodes.get(ref)!; - const obj = this.props.plugin.state.data.objects.get(ref)!; + const obj = this.props.plugin.state.data.cells.get(ref)!; const type = obj && obj.obj ? obj.obj.type : void 0; diff --git a/src/mol-plugin/ui/state-tree.tsx b/src/mol-plugin/ui/state-tree.tsx index 8c7dc5dd0..715972557 100644 --- a/src/mol-plugin/ui/state-tree.tsx +++ b/src/mol-plugin/ui/state-tree.tsx @@ -28,20 +28,21 @@ export class StateTree extends React.Component<{ plugin: PluginContext, state: S export class StateTreeNode extends React.Component<{ plugin: PluginContext, nodeRef: string, state: State }, { }> { render() { const n = this.props.state.tree.nodes.get(this.props.nodeRef)!; - const obj = this.props.state.objects.get(this.props.nodeRef)!; + const cell = this.props.state.cells.get(this.props.nodeRef)!; const remove = <>[<a href='#' onClick={e => { e.preventDefault(); PluginCommands.Data.RemoveObject.dispatch(this.props.plugin, { ref: this.props.nodeRef }); }}>X</a>]</> - if (obj.status !== 'ok' || !obj.obj) { + if (cell.status !== 'ok' || !cell.obj) { return <div style={{ borderLeft: '1px solid black', paddingLeft: '7px' }}> - {remove} {obj.status} {obj.errorText} + {remove} {cell.status} {cell.errorText} </div>; } - const props = obj.obj!.props as PluginStateObject.Props; - const type = obj.obj!.type.info as PluginStateObject.TypeInfo; + const obj = cell.obj as PluginStateObject.Any; + const props = obj.props; + const type = obj.type; return <div style={{ borderLeft: '0px solid #999', paddingLeft: '0px' }}> {remove}[<span title={type.description}>{ type.shortName }</span>] <a href='#' onClick={e => { e.preventDefault(); diff --git a/src/mol-state/object.ts b/src/mol-state/object.ts index ed74ec092..fcd4581ec 100644 --- a/src/mol-state/object.ts +++ b/src/mol-state/object.ts @@ -7,49 +7,45 @@ import { UUID } from 'mol-util'; import { Transform } from './transform'; -export { StateObject, StateObjectBox } +export { StateObject, StateObjectCell } -interface StateObject<P = any, D = any, Type = { }> { +interface StateObject<P = any, D = any, T = any> { readonly id: UUID, - readonly type: StateObject.Type, + readonly type: StateObject.Type<T>, readonly props: P, readonly data: D } namespace StateObject { - export interface Type<Info = any> { - info: Info + export function factory<Type, CommonProps>() { + return <D = { }, P = {}>(type: Type) => create<P & CommonProps, D, Type>(type); } - export function factory<TypeInfo, CommonProps>() { - return <D = { }, P = {}>(typeInfo: TypeInfo) => create<P & CommonProps, D, TypeInfo>(typeInfo); - } - - export type Ctor = { new(...args: any[]): StateObject, type: Type } + export type Type<I = unknown> = I + export type Ctor = { new(...args: any[]): StateObject, type: any } - export function create<Props, Data, TypeInfo>(typeInfo: TypeInfo) { - const dataType: Type<TypeInfo> = { info: typeInfo }; - return class implements StateObject<Props, Data, Type<TypeInfo>> { - static type = dataType; - static is(obj?: StateObject): obj is StateObject<Props, Data> { return !!obj && dataType === obj.type; } + export function create<Props, Data, Type>(type: Type) { + return class implements StateObject<Props, Data, Type> { + static type = type; + static is(obj?: StateObject): obj is StateObject<Props, Data, Type> { return !!obj && type === obj.type; } id = UUID.create(); - type = dataType; + type = type; constructor(public props: Props, public data: Data) { } } } } -interface StateObjectBox { +interface StateObjectCell { ref: Transform.Ref, props: unknown, - status: StateObjectBox.Status, + status: StateObjectCell.Status, errorText?: string, obj?: StateObject, version: string } -namespace StateObjectBox { +namespace StateObjectCell { export type Status = 'ok' | 'error' | 'pending' | 'processing' export interface Props { diff --git a/src/mol-state/selection.ts b/src/mol-state/selection.ts index 560a0ef4c..e7eb61d63 100644 --- a/src/mol-state/selection.ts +++ b/src/mol-state/selection.ts @@ -4,14 +4,14 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { StateObject, StateObjectBox } from './object'; +import { StateObject, StateObjectCell } from './object'; import { State } from './state'; import { ImmutableTree } from './immutable-tree'; namespace StateSelection { - export type Selector = Query | Builder | string | StateObjectBox; - export type NodeSeq = StateObjectBox[] - export type Query = (state: State) => NodeSeq; + export type Selector = Query | Builder | string | StateObjectCell; + export type CellSeq = StateObjectCell[] + export type Query = (state: State) => CellSeq; export function select(s: Selector, state: State) { return compile(s)(state); @@ -27,8 +27,8 @@ namespace StateSelection { return query; } - function isObj(arg: any): arg is StateObjectBox { - return (arg as StateObjectBox).version !== void 0; + function isObj(arg: any): arg is StateObjectCell { + return (arg as StateObjectCell).version !== void 0; } function isBuilder(arg: any): arg is Builder { @@ -40,13 +40,13 @@ namespace StateSelection { } export interface Builder { - flatMap(f: (n: StateObjectBox) => StateObjectBox[]): Builder; - mapEntity(f: (n: StateObjectBox) => StateObjectBox): Builder; + flatMap(f: (n: StateObjectCell) => StateObjectCell[]): Builder; + mapEntity(f: (n: StateObjectCell) => StateObjectCell): Builder; unique(): Builder; parent(): Builder; first(): Builder; - filter(p: (n: StateObjectBox) => boolean): Builder; + filter(p: (n: StateObjectCell) => boolean): Builder; subtree(): Builder; children(): Builder; ofType(t: StateObject.Type): Builder; @@ -63,14 +63,14 @@ namespace StateSelection { return Object.create(BuilderPrototype, { compile: { writable: false, configurable: false, value: compile } }); } - export function root() { return build(() => (state: State) => [state.objects.get(state.tree.rootRef)!]) } + export function root() { return build(() => (state: State) => [state.cells.get(state.tree.rootRef)!]) } export function byRef(...refs: string[]) { return build(() => (state: State) => { - const ret: StateObjectBox[] = []; + const ret: StateObjectCell[] = []; for (const ref of refs) { - const n = state.objects.get(ref); + const n = state.cells.get(ref); if (!n) continue; ret.push(n); } @@ -78,13 +78,13 @@ namespace StateSelection { }); } - export function byValue(...objects: StateObjectBox[]) { return build(() => (state: State) => objects); } + export function byValue(...objects: StateObjectCell[]) { return build(() => (state: State) => objects); } registerModifier('flatMap', flatMap); - export function flatMap(b: Selector, f: (obj: StateObjectBox, state: State) => NodeSeq) { + export function flatMap(b: Selector, f: (obj: StateObjectCell, state: State) => CellSeq) { const q = compile(b); return build(() => (state: State) => { - const ret: StateObjectBox[] = []; + const ret: StateObjectCell[] = []; for (const n of q(state)) { for (const m of f(n, state)) { ret.push(m); @@ -95,10 +95,10 @@ namespace StateSelection { } registerModifier('mapEntity', mapEntity); - export function mapEntity(b: Selector, f: (n: StateObjectBox, state: State) => StateObjectBox | undefined) { + export function mapEntity(b: Selector, f: (n: StateObjectCell, state: State) => StateObjectCell | undefined) { const q = compile(b); return build(() => (state: State) => { - const ret: StateObjectBox[] = []; + const ret: StateObjectCell[] = []; for (const n of q(state)) { const x = f(n, state); if (x) ret.push(x); @@ -112,7 +112,7 @@ namespace StateSelection { const q = compile(b); return build(() => (state: State) => { const set = new Set<string>(); - const ret: StateObjectBox[] = []; + const ret: StateObjectCell[] = []; for (const n of q(state)) { if (!set.has(n.ref)) { set.add(n.ref); @@ -133,22 +133,22 @@ namespace StateSelection { } registerModifier('filter', filter); - export function filter(b: Selector, p: (n: StateObjectBox) => boolean) { return flatMap(b, n => p(n) ? [n] : []); } + export function filter(b: Selector, p: (n: StateObjectCell) => boolean) { return flatMap(b, n => p(n) ? [n] : []); } registerModifier('subtree', subtree); export function subtree(b: Selector) { return flatMap(b, (n, s) => { const nodes = [] as string[]; ImmutableTree.doPreOrder(s.tree, s.tree.nodes.get(n.ref), nodes, (x, _, ctx) => { ctx.push(x.ref) }); - return nodes.map(x => s.objects.get(x)!); + return nodes.map(x => s.cells.get(x)!); }); } registerModifier('children', children); export function children(b: Selector) { return flatMap(b, (n, s) => { - const nodes: StateObjectBox[] = []; - s.tree.nodes.get(n.ref)!.children.forEach(c => nodes.push(s.objects.get(c!)!)); + const nodes: StateObjectCell[] = []; + s.tree.nodes.get(n.ref)!.children.forEach(c => nodes.push(s.cells.get(c!)!)); return nodes; }); } @@ -160,17 +160,17 @@ namespace StateSelection { export function ancestorOfType(b: Selector, t: StateObject.Type) { return unique(mapEntity(b, (n, s) => findAncestorOfType(s, n.ref, t))); } registerModifier('parent', parent); - export function parent(b: Selector) { return unique(mapEntity(b, (n, s) => s.objects.get(s.tree.nodes.get(n.ref)!.parent))); } + export function parent(b: Selector) { return unique(mapEntity(b, (n, s) => s.cells.get(s.tree.nodes.get(n.ref)!.parent))); } - function findAncestorOfType({ tree, objects }: State, root: string, type: StateObject.Type): StateObjectBox | undefined { + function findAncestorOfType({ tree, cells }: State, root: string, type: StateObject.Type): StateObjectCell | undefined { let current = tree.nodes.get(root)!; while (true) { current = tree.nodes.get(current.parent)!; if (current.ref === tree.rootRef) { - return objects.get(tree.rootRef); + return cells.get(tree.rootRef); } - const obj = objects.get(current.ref)!.obj!; - if (obj.type === type) return objects.get(current.ref); + const obj = cells.get(current.ref)!.obj!; + if (obj.type === type) return cells.get(current.ref); } } } diff --git a/src/mol-state/state.ts b/src/mol-state/state.ts index aecbbcb65..5851edbd9 100644 --- a/src/mol-state/state.ts +++ b/src/mol-state/state.ts @@ -4,7 +4,7 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { StateObject, StateObjectBox } from './object'; +import { StateObject, StateObjectCell } from './object'; import { StateTree } from './tree'; import { Transform } from './transform'; import { ImmutableTree } from './immutable-tree'; @@ -23,16 +23,16 @@ class State { get tree() { return this._tree; } get current() { return this._current; } - readonly objects: State.Objects = new Map(); + readonly cells: State.Cells = new Map(); readonly context: StateContext; getSnapshot(): State.Snapshot { const props = Object.create(null); - const keys = this.objects.keys(); + const keys = this.cells.keys(); while (true) { const key = keys.next(); if (key.done) break; - const o = this.objects.get(key.value)!; + const o = this.cells.get(key.value)!; props[key.value] = { ...o.props }; } return { @@ -68,7 +68,7 @@ class State { taskCtx, oldTree, tree: tree, - objects: this.objects, + objects: this.cells, transformCache: this.transformCache }; // TODO: have "cancelled" error? Or would this be handled automatically? @@ -84,7 +84,7 @@ class State { const root = tree.getValue(tree.rootRef)!; const defaultObjectProps = (params && params.defaultObjectProps) || { } - this.objects.set(tree.rootRef, { + this.cells.set(tree.rootRef, { ref: tree.rootRef, obj: rootObject, status: 'ok', @@ -101,7 +101,7 @@ class State { } namespace State { - export type Objects = Map<Transform.Ref, StateObjectBox> + export type Cells = Map<Transform.Ref, StateObjectCell> export interface Snapshot { readonly tree: StateTree.Serialized, @@ -120,7 +120,7 @@ namespace State { taskCtx: RuntimeContext, oldTree: StateTree, tree: StateTree, - objects: State.Objects, + objects: State.Cells, transformCache: Map<Ref, unknown> } @@ -142,7 +142,7 @@ namespace State { } } - function findUpdateRoots(objects: State.Objects, tree: StateTree) { + function findUpdateRoots(objects: State.Cells, tree: StateTree) { const findState = { roots: [] as Ref[], objects @@ -177,7 +177,7 @@ namespace State { return deletes; } - function setObjectState(ctx: UpdateContext, ref: Ref, status: StateObjectBox.Status, errorText?: string) { + function setObjectState(ctx: UpdateContext, ref: Ref, status: StateObjectCell.Status, errorText?: string) { let changed = false; if (ctx.objects.has(ref)) { const obj = ctx.objects.get(ref)!; @@ -185,7 +185,7 @@ namespace State { obj.status = status; obj.errorText = errorText; } else { - const obj: StateObjectBox = { ref, status, version: UUID.create(), errorText, props: { ...ctx.stateCtx.defaultObjectProps } }; + const obj: StateObjectCell = { ref, status, version: UUID.create(), errorText, props: { ...ctx.stateCtx.defaultObjectProps } }; ctx.objects.set(ref, obj); changed = true; } @@ -219,7 +219,7 @@ namespace State { } } - function findAncestor(tree: StateTree, objects: State.Objects, root: Ref, types: { type: StateObject.Type }[]): StateObject { + function findAncestor(tree: StateTree, objects: State.Cells, root: Ref, types: { type: StateObject.Type }[]): StateObject { let current = tree.nodes.get(root)!; while (true) { current = tree.nodes.get(current.parent)!; diff --git a/src/perf-tests/state.ts b/src/perf-tests/state.ts index dbe511f90..799c28dc6 100644 --- a/src/perf-tests/state.ts +++ b/src/perf-tests/state.ts @@ -68,7 +68,7 @@ function hookEvents(state: State) { state.context.events.object.created.subscribe(e => console.log('created:', e.ref)); state.context.events.object.removed.subscribe(e => console.log('removed:', e.ref)); state.context.events.object.replaced.subscribe(e => console.log('replaced:', e.ref)); - state.context.events.object.stateChanged.subscribe(e => console.log('stateChanged:', e.ref, state.objects.get(e.ref)!.status)); + state.context.events.object.stateChanged.subscribe(e => console.log('stateChanged:', e.ref, state.cells.get(e.ref)!.status)); state.context.events.object.updated.subscribe(e => console.log('updated:', e.ref)); } @@ -91,7 +91,7 @@ export async function testState() { await state.update(tree1).run(); console.log('----------------'); - console.log(util.inspect(state.objects, true, 3, true)); + console.log(util.inspect(state.cells, true, 3, true)); console.log('----------------'); const jsonString = JSON.stringify(StateTree.toJSON(tree2), null, 2); @@ -103,7 +103,7 @@ export async function testState() { console.log('----------------'); await state.update(treeFromJson).run(); - console.log(util.inspect(state.objects, true, 3, true)); + console.log(util.inspect(state.cells, true, 3, true)); console.log('----------------'); -- GitLab