diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts index 5355bfc351a17b13970dac120d65dabd9e26bc08..4b2fc4a1ceef36cde87a442d7ffe6f98b1804daa 100644 --- a/src/mol-plugin/context.ts +++ b/src/mol-plugin/context.ts @@ -4,32 +4,28 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { State, StateTree, StateSelection, Transformer } from 'mol-state'; +import { StateTree, StateSelection, Transformer } from 'mol-state'; import Canvas3D from 'mol-canvas3d/canvas3d'; import { StateTransforms } from './state/transforms'; import { PluginStateObjects as SO } from './state/objects'; import { RxEventHelper } from 'mol-util/rx-event-helper'; +import { PluginState } from './state'; export class PluginContext { private disposed = false; - private _events = new RxEventHelper(); + private ev = RxEventHelper.create(); - state = { - data: State.create(new SO.Root({ label: 'Root' }, { })), - // behaviour: State, - // plugin: State - }; + readonly state = new PluginState(); - // TODO: better events - events = { - stateUpdated: this._events.create<undefined>() + readonly events = { + stateUpdated: this.ev<undefined>() }; - canvas3d: Canvas3D; + readonly canvas3d: Canvas3D; initViewer(canvas: HTMLCanvasElement, container: HTMLDivElement) { try { - this.canvas3d = Canvas3D.create(canvas, container); + (this.canvas3d as Canvas3D) = Canvas3D.create(canvas, container); this.canvas3d.animate(); console.log('canvas3d created'); return true; @@ -42,7 +38,8 @@ export class PluginContext { dispose() { if (this.disposed) return; this.canvas3d.dispose(); - this._events.dispose(); + this.ev.dispose(); + this.state.dispose(); this.disposed = true; } @@ -60,9 +57,8 @@ export class PluginContext { } async _test_updateStateData(tree: StateTree) { - const newState = await State.update(this.state.data, tree).run(p => console.log(p), 250); - this.state.data = newState; - console.log(newState); + await this.state.data.update(tree).run(p => console.log(p), 250); + console.log(this.state.data); this.events.stateUpdated.next(); } diff --git a/src/mol-plugin/state.ts b/src/mol-plugin/state.ts new file mode 100644 index 0000000000000000000000000000000000000000..b45f9b86196c1988441925e8e3db878d92bcc9c7 --- /dev/null +++ b/src/mol-plugin/state.ts @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { State } from 'mol-state'; +import { PluginStateObjects as SO } from './state/objects'; + +export { PluginState } + +class PluginState { + readonly data = State.create(new SO.Root({ label: 'Root' }, { })); + + getSnapshot(): PluginState.Snapshot { + throw 'nyi'; + } + + setSnapshot(snapshot: PluginState.Snapshot) { + throw 'nyi'; + } + + setDataSnapshot(snapshot: State.Snapshot) { + throw 'nyi'; + } + + dispose() { + this.data.dispose(); + } +} + +namespace PluginState { + export interface Snapshot { } +} diff --git a/src/mol-state/context.ts b/src/mol-state/context.ts index 10117db026fadef9d44c62facd7885fc855b3cd7..ff9acff79b16b823acbc9587e105abb89de3167c 100644 --- a/src/mol-state/context.ts +++ b/src/mol-state/context.ts @@ -4,45 +4,37 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Subject } from 'rxjs' import { StateObject } from './object'; import { Transform } from './transform'; +import { RxEventHelper } from 'mol-util/rx-event-helper'; -interface StateContext { - events: { +export { StateContext } + +class StateContext { + private ev = RxEventHelper.create(); + + events = { object: { - stateChanged: Subject<{ ref: Transform.Ref }>, - propsChanged: Subject<{ ref: Transform.Ref, newProps: unknown }>, + stateChanged: this.ev<{ ref: Transform.Ref }>(), + propsChanged: this.ev<{ ref: Transform.Ref, newProps: unknown }>(), - updated: Subject<{ ref: Transform.Ref, obj?: StateObject }>, - replaced: Subject<{ ref: Transform.Ref, oldObj?: StateObject, newObj?: StateObject }>, - created: Subject<{ ref: Transform.Ref, obj: StateObject }>, - removed: Subject<{ ref: Transform.Ref, obj?: StateObject }>, + updated: this.ev<{ ref: Transform.Ref, obj?: StateObject }>(), + replaced: this.ev<{ ref: Transform.Ref, oldObj?: StateObject, newObj?: StateObject }>(), + created: this.ev<{ ref: Transform.Ref, obj: StateObject }>(), + removed: this.ev<{ ref: Transform.Ref, obj?: StateObject }>(), }, - warn: Subject<string> - }, - globalContext: unknown, - defaultObjectProps: unknown -} - -namespace StateContext { - export function create(params: { globalContext: unknown, defaultObjectProps: unknown }): StateContext { - return { - events: { - object: { - stateChanged: new Subject(), - propsChanged: new Subject(), - updated: new Subject(), - replaced: new Subject(), - created: new Subject(), - removed: new Subject() - }, - warn: new Subject() - }, - globalContext: params.globalContext, - defaultObjectProps: params.defaultObjectProps - } + warn: this.ev<string>() + }; + + readonly globalContext: unknown; + readonly defaultObjectProps: unknown; + + dispose() { + this.ev.dispose(); } -} -export { StateContext } \ No newline at end of file + constructor(params: { globalContext: unknown, defaultObjectProps: unknown }) { + this.globalContext = params.globalContext; + this.defaultObjectProps = params.defaultObjectProps; + } +} \ No newline at end of file diff --git a/src/mol-state/state.ts b/src/mol-state/state.ts index 75fa6aaef3fa9cf41d2e715cd1f6965868613bbc..d59f98ce078b6fa04770092cacb3087545c70a89 100644 --- a/src/mol-state/state.ts +++ b/src/mol-state/state.ts @@ -13,23 +13,50 @@ import { StateContext } from './context'; import { UUID } from 'mol-util'; import { RuntimeContext, Task } from 'mol-task'; -export interface State { - tree: StateTree, - objects: State.Objects, - context: StateContext -} +export { State } -export namespace State { - export type Ref = Transform.Ref - export type Objects = Map<Ref, StateObject.Node> +class State { + private _tree: StateTree = StateTree.create(); + get tree() { return this._tree; } - export function create(rootObject: StateObject, params?: { globalContext?: unknown, defaultObjectProps: unknown }) { - const tree = StateTree.create(); - const objects: Objects = new Map(); + readonly objects: State.Objects = new Map(); + readonly context: StateContext; + + getSnapshot(): State.Snapshot { + throw 'nyi'; + } + + setSnapshot(snapshot: State.Snapshot): void { + throw 'nyi'; + } + + dispose() { + this.context.dispose(); + } + + update(tree: StateTree): Task<void> { + return Task.create('Update Tree', taskCtx => { + const oldTree = this._tree; + this._tree = tree; + + const ctx: UpdateContext = { + stateCtx: this.context, + taskCtx, + oldTree, + tree: tree, + objects: this.objects + }; + // TODO: have "cancelled" error? Or would this be handled automatically? + return update(ctx); + }); + } + + constructor(rootObject: StateObject, params?: { globalContext?: unknown, defaultObjectProps: unknown }) { + const tree = this._tree; const root = tree.getValue(tree.rootRef)!; const defaultObjectProps = (params && params.defaultObjectProps) || { } - objects.set(tree.rootRef, { + this.objects.set(tree.rootRef, { ref: tree.rootRef, obj: rootObject, state: StateObject.StateType.Ok, @@ -37,30 +64,37 @@ export namespace State { props: { ...defaultObjectProps } }); - return { - tree, - objects, - context: StateContext.create({ - globalContext: params && params.globalContext, - defaultObjectProps - }) - }; + this.context = new StateContext({ + globalContext: params && params.globalContext, + defaultObjectProps + }); } +} - export function update(state: State, tree: StateTree): Task<State> { - return Task.create('Update Tree', taskCtx => { - const ctx: UpdateContext = { - stateCtx: state.context, - taskCtx, - oldTree: state.tree, - tree: tree, - objects: state.objects - }; - return _update(ctx); - }) +namespace State { + export type Objects = Map<Transform.Ref, StateObject.Node> + + export interface Snapshot { + readonly tree: StateTree, + readonly props: { [key: string]: unknown } } - async function _update(ctx: UpdateContext): Promise<State> { + export function create(rootObject: StateObject, params?: { globalContext?: unknown, defaultObjectProps: unknown }) { + return new State(rootObject, params); + } +} + + type Ref = Transform.Ref + + interface UpdateContext { + stateCtx: StateContext, + taskCtx: RuntimeContext, + oldTree: StateTree, + tree: StateTree, + objects: State.Objects + } + + async function update(ctx: UpdateContext) { const roots = findUpdateRoots(ctx.objects, ctx.tree); const deletes = findDeletes(ctx); for (const d of deletes) { @@ -73,23 +107,9 @@ export namespace State { for (const root of roots) { await updateSubtree(ctx, root); } - - return { - tree: ctx.tree, - objects: ctx.objects, - context: ctx.stateCtx - }; - } - - interface UpdateContext { - stateCtx: StateContext, - taskCtx: RuntimeContext, - oldTree: StateTree, - tree: StateTree, - objects: Objects } - function findUpdateRoots(objects: Objects, tree: StateTree) { + function findUpdateRoots(objects: State.Objects, tree: StateTree) { const findState = { roots: [] as Ref[], objects @@ -165,7 +185,7 @@ export namespace State { } } - function findAncestor(tree: StateTree, objects: Objects, root: Ref, types: { type: StateObject.Type }[]): StateObject { + function findAncestor(tree: StateTree, objects: State.Objects, root: Ref, types: { type: StateObject.Type }[]): StateObject { let current = tree.nodes.get(root)!; while (true) { current = tree.nodes.get(current.parent)!; @@ -260,5 +280,4 @@ export namespace State { return Transformer.UpdateResult.Recreate; } return runTask(transformer.definition.update({ a, oldParams, b, newParams }, ctx.stateCtx.globalContext), ctx.taskCtx); - } -} + } \ No newline at end of file diff --git a/src/mol-util/rx-event-helper.ts b/src/mol-util/rx-event-helper.ts index 4014f03a9f85d0311fd16fd071506e4972d68a85..c24a16be7b89335bd0c3209791e7a6902d98d69d 100644 --- a/src/mol-util/rx-event-helper.ts +++ b/src/mol-util/rx-event-helper.ts @@ -6,14 +6,34 @@ import { Subject } from 'rxjs'; -export class RxEventHelper { +export { RxEventHelper } + +interface RxEventHelper { + <T>(): Subject<T>, + dispose(): void +} + +namespace RxEventHelper { + export function create(): RxEventHelper { + const helper = new _RxEventHelper(); + const ret: RxEventHelper = (<T>() => helper.create<T>()) as RxEventHelper; + ret.dispose = () => helper.dispose(); + return ret; + } +} + +class _RxEventHelper { private _eventList: Subject<any>[] = []; + private _disposed = false; + create<T>() { const s = new Subject<T>(); this._eventList.push(s); return s; } dispose() { + if (this._disposed) return; for (const e of this._eventList) e.complete(); + this._disposed = true; } } \ No newline at end of file diff --git a/src/perf-tests/state.ts b/src/perf-tests/state.ts index 98067b5850e756244e27524b5729f5cd48145ee8..dd594d26c88e747ed8c30b3fa97db41986965ad3 100644 --- a/src/perf-tests/state.ts +++ b/src/perf-tests/state.ts @@ -90,9 +90,9 @@ export async function testState() { printTTree(tree1); printTTree(tree2); - const state1 = await State.update(state, tree1).run(); + await state.update(tree1).run(); console.log('----------------'); - console.log(util.inspect(state1.objects, true, 3, true)); + console.log(util.inspect(state.objects, true, 3, true)); console.log('----------------'); const jsonString = JSON.stringify(StateTree.toJSON(tree2), null, 2); @@ -103,13 +103,13 @@ export async function testState() { printTTree(treeFromJson); console.log('----------------'); - const state2 = await State.update(state1, treeFromJson).run(); - console.log(util.inspect(state2.objects, true, 3, true)); + await state.update(treeFromJson).run(); + console.log(util.inspect(state.objects, true, 3, true)); console.log('----------------'); const q = StateSelection.byRef('square').parent(); - const sel = StateSelection.select(q, state2); + const sel = StateSelection.select(q, state); console.log(sel); }