Skip to content
Snippets Groups Projects
Commit fd3aade5 authored by David Sehnal's avatar David Sehnal
Browse files

mol-state and mol-plugin refactoring

parent 81bf0083
No related branches found
No related tags found
No related merge requests found
...@@ -4,32 +4,28 @@ ...@@ -4,32 +4,28 @@
* @author David Sehnal <david.sehnal@gmail.com> * @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 Canvas3D from 'mol-canvas3d/canvas3d';
import { StateTransforms } from './state/transforms'; import { StateTransforms } from './state/transforms';
import { PluginStateObjects as SO } from './state/objects'; import { PluginStateObjects as SO } from './state/objects';
import { RxEventHelper } from 'mol-util/rx-event-helper'; import { RxEventHelper } from 'mol-util/rx-event-helper';
import { PluginState } from './state';
export class PluginContext { export class PluginContext {
private disposed = false; private disposed = false;
private _events = new RxEventHelper(); private ev = RxEventHelper.create();
state = { readonly state = new PluginState();
data: State.create(new SO.Root({ label: 'Root' }, { })),
// behaviour: State,
// plugin: State
};
// TODO: better events readonly events = {
events = { stateUpdated: this.ev<undefined>()
stateUpdated: this._events.create<undefined>()
}; };
canvas3d: Canvas3D; readonly canvas3d: Canvas3D;
initViewer(canvas: HTMLCanvasElement, container: HTMLDivElement) { initViewer(canvas: HTMLCanvasElement, container: HTMLDivElement) {
try { try {
this.canvas3d = Canvas3D.create(canvas, container); (this.canvas3d as Canvas3D) = Canvas3D.create(canvas, container);
this.canvas3d.animate(); this.canvas3d.animate();
console.log('canvas3d created'); console.log('canvas3d created');
return true; return true;
...@@ -42,7 +38,8 @@ export class PluginContext { ...@@ -42,7 +38,8 @@ export class PluginContext {
dispose() { dispose() {
if (this.disposed) return; if (this.disposed) return;
this.canvas3d.dispose(); this.canvas3d.dispose();
this._events.dispose(); this.ev.dispose();
this.state.dispose();
this.disposed = true; this.disposed = true;
} }
...@@ -60,9 +57,8 @@ export class PluginContext { ...@@ -60,9 +57,8 @@ export class PluginContext {
} }
async _test_updateStateData(tree: StateTree) { async _test_updateStateData(tree: StateTree) {
const newState = await State.update(this.state.data, tree).run(p => console.log(p), 250); await this.state.data.update(tree).run(p => console.log(p), 250);
this.state.data = newState; console.log(this.state.data);
console.log(newState);
this.events.stateUpdated.next(); this.events.stateUpdated.next();
} }
......
/**
* 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 { }
}
...@@ -4,45 +4,37 @@ ...@@ -4,45 +4,37 @@
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
*/ */
import { Subject } from 'rxjs'
import { StateObject } from './object'; import { StateObject } from './object';
import { Transform } from './transform'; import { Transform } from './transform';
import { RxEventHelper } from 'mol-util/rx-event-helper';
interface StateContext { export { StateContext }
events: {
class StateContext {
private ev = RxEventHelper.create();
events = {
object: { object: {
stateChanged: Subject<{ ref: Transform.Ref }>, stateChanged: this.ev<{ ref: Transform.Ref }>(),
propsChanged: Subject<{ ref: Transform.Ref, newProps: unknown }>, propsChanged: this.ev<{ ref: Transform.Ref, newProps: unknown }>(),
updated: Subject<{ ref: Transform.Ref, obj?: StateObject }>, updated: this.ev<{ ref: Transform.Ref, obj?: StateObject }>(),
replaced: Subject<{ ref: Transform.Ref, oldObj?: StateObject, newObj?: StateObject }>, replaced: this.ev<{ ref: Transform.Ref, oldObj?: StateObject, newObj?: StateObject }>(),
created: Subject<{ ref: Transform.Ref, obj: StateObject }>, created: this.ev<{ ref: Transform.Ref, obj: StateObject }>(),
removed: Subject<{ ref: Transform.Ref, obj?: StateObject }>, removed: this.ev<{ ref: Transform.Ref, obj?: StateObject }>(),
}, },
warn: Subject<string> warn: this.ev<string>()
}, };
globalContext: unknown,
defaultObjectProps: unknown readonly globalContext: unknown;
} readonly defaultObjectProps: unknown;
namespace StateContext { dispose() {
export function create(params: { globalContext: unknown, defaultObjectProps: unknown }): StateContext { this.ev.dispose();
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
}
} }
}
export { StateContext } constructor(params: { globalContext: unknown, defaultObjectProps: unknown }) {
\ No newline at end of file this.globalContext = params.globalContext;
this.defaultObjectProps = params.defaultObjectProps;
}
}
\ No newline at end of file
...@@ -13,23 +13,50 @@ import { StateContext } from './context'; ...@@ -13,23 +13,50 @@ import { StateContext } from './context';
import { UUID } from 'mol-util'; import { UUID } from 'mol-util';
import { RuntimeContext, Task } from 'mol-task'; import { RuntimeContext, Task } from 'mol-task';
export interface State { export { State }
tree: StateTree,
objects: State.Objects,
context: StateContext
}
export namespace State { class State {
export type Ref = Transform.Ref private _tree: StateTree = StateTree.create();
export type Objects = Map<Ref, StateObject.Node> get tree() { return this._tree; }
export function create(rootObject: StateObject, params?: { globalContext?: unknown, defaultObjectProps: unknown }) { readonly objects: State.Objects = new Map();
const tree = StateTree.create(); readonly context: StateContext;
const objects: Objects = new Map();
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 root = tree.getValue(tree.rootRef)!;
const defaultObjectProps = (params && params.defaultObjectProps) || { } const defaultObjectProps = (params && params.defaultObjectProps) || { }
objects.set(tree.rootRef, { this.objects.set(tree.rootRef, {
ref: tree.rootRef, ref: tree.rootRef,
obj: rootObject, obj: rootObject,
state: StateObject.StateType.Ok, state: StateObject.StateType.Ok,
...@@ -37,30 +64,37 @@ export namespace State { ...@@ -37,30 +64,37 @@ export namespace State {
props: { ...defaultObjectProps } props: { ...defaultObjectProps }
}); });
return { this.context = new StateContext({
tree, globalContext: params && params.globalContext,
objects, defaultObjectProps
context: StateContext.create({ });
globalContext: params && params.globalContext,
defaultObjectProps
})
};
} }
}
export function update(state: State, tree: StateTree): Task<State> { namespace State {
return Task.create('Update Tree', taskCtx => { export type Objects = Map<Transform.Ref, StateObject.Node>
const ctx: UpdateContext = {
stateCtx: state.context, export interface Snapshot {
taskCtx, readonly tree: StateTree,
oldTree: state.tree, readonly props: { [key: string]: unknown }
tree: tree,
objects: state.objects
};
return _update(ctx);
})
} }
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 roots = findUpdateRoots(ctx.objects, ctx.tree);
const deletes = findDeletes(ctx); const deletes = findDeletes(ctx);
for (const d of deletes) { for (const d of deletes) {
...@@ -73,23 +107,9 @@ export namespace State { ...@@ -73,23 +107,9 @@ export namespace State {
for (const root of roots) { for (const root of roots) {
await updateSubtree(ctx, root); 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 = { const findState = {
roots: [] as Ref[], roots: [] as Ref[],
objects objects
...@@ -165,7 +185,7 @@ export namespace State { ...@@ -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)!; let current = tree.nodes.get(root)!;
while (true) { while (true) {
current = tree.nodes.get(current.parent)!; current = tree.nodes.get(current.parent)!;
...@@ -260,5 +280,4 @@ export namespace State { ...@@ -260,5 +280,4 @@ export namespace State {
return Transformer.UpdateResult.Recreate; return Transformer.UpdateResult.Recreate;
} }
return runTask(transformer.definition.update({ a, oldParams, b, newParams }, ctx.stateCtx.globalContext), ctx.taskCtx); return runTask(transformer.definition.update({ a, oldParams, b, newParams }, ctx.stateCtx.globalContext), ctx.taskCtx);
} }
} \ No newline at end of file
...@@ -6,14 +6,34 @@ ...@@ -6,14 +6,34 @@
import { Subject } from 'rxjs'; 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 _eventList: Subject<any>[] = [];
private _disposed = false;
create<T>() { create<T>() {
const s = new Subject<T>(); const s = new Subject<T>();
this._eventList.push(s); this._eventList.push(s);
return s; return s;
} }
dispose() { dispose() {
if (this._disposed) return;
for (const e of this._eventList) e.complete(); for (const e of this._eventList) e.complete();
this._disposed = true;
} }
} }
\ No newline at end of file
...@@ -90,9 +90,9 @@ export async function testState() { ...@@ -90,9 +90,9 @@ export async function testState() {
printTTree(tree1); printTTree(tree1);
printTTree(tree2); printTTree(tree2);
const state1 = await State.update(state, tree1).run(); await state.update(tree1).run();
console.log('----------------'); console.log('----------------');
console.log(util.inspect(state1.objects, true, 3, true)); console.log(util.inspect(state.objects, true, 3, true));
console.log('----------------'); console.log('----------------');
const jsonString = JSON.stringify(StateTree.toJSON(tree2), null, 2); const jsonString = JSON.stringify(StateTree.toJSON(tree2), null, 2);
...@@ -103,13 +103,13 @@ export async function testState() { ...@@ -103,13 +103,13 @@ export async function testState() {
printTTree(treeFromJson); printTTree(treeFromJson);
console.log('----------------'); console.log('----------------');
const state2 = await State.update(state1, treeFromJson).run(); await state.update(treeFromJson).run();
console.log(util.inspect(state2.objects, true, 3, true)); console.log(util.inspect(state.objects, true, 3, true));
console.log('----------------'); console.log('----------------');
const q = StateSelection.byRef('square').parent(); const q = StateSelection.byRef('square').parent();
const sel = StateSelection.select(q, state2); const sel = StateSelection.select(q, state);
console.log(sel); console.log(sel);
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment