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

mol-state: added StateAction

parent 394817e2
No related branches found
No related tags found
No related merge requests found
Showing
with 146 additions and 105 deletions
......@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { PluginStateTransform, PluginStateObject } from '../state/base';
import { PluginStateTransform, PluginStateObject } from '../state/objects';
import { Transformer } from 'mol-state';
import { Task } from 'mol-task';
import { PluginContext } from 'mol-plugin/context';
......
......@@ -5,7 +5,7 @@
*/
import { PluginBehavior } from '../behavior';
import { PluginStateObject as SO } from '../../state/base';
import { PluginStateObject as SO } from '../../state/objects';
import { EmptyLoci, Loci, areLociEqual } from 'mol-model/loci';
import { MarkerAction } from 'mol-geo/geometry/marker-data';
......
......@@ -7,8 +7,7 @@
import { Transformer, Transform, State } from 'mol-state';
import { Canvas3D } from 'mol-canvas3d/canvas3d';
import { StateTransforms } from './state/transforms';
import { PluginStateObject as PSO } from './state/base';
import { PluginStateObjects as SO } from './state/objects';
import { PluginStateObject as SO } from './state/objects';
import { RxEventHelper } from 'mol-util/rx-event-helper';
import { PluginState } from './state';
import { MolScriptBuilder } from 'mol-script/language/builder';
......@@ -135,19 +134,19 @@ export class PluginContext {
private initEvents() {
merge(this.events.state.data.object.created, this.events.state.behavior.object.created).subscribe(o => {
if (!PSO.isBehavior(o.obj)) return;
if (!SO.isBehavior(o.obj)) return;
console.log('registering behavior', o.obj.label);
o.obj.data.register();
});
merge(this.events.state.data.object.removed, this.events.state.behavior.object.removed).subscribe(o => {
if (!PSO.isBehavior(o.obj)) return;
if (!SO.isBehavior(o.obj)) return;
o.obj.data.unregister();
});
merge(this.events.state.data.object.replaced, this.events.state.behavior.object.replaced).subscribe(o => {
if (o.oldObj && PSO.isBehavior(o.oldObj)) o.oldObj.data.unregister();
if (o.newObj && PSO.isBehavior(o.newObj)) o.newObj.data.register();
if (o.oldObj && SO.isBehavior(o.oldObj)) o.oldObj.data.unregister();
if (o.newObj && SO.isBehavior(o.newObj)) o.newObj.data.register();
});
}
......
......@@ -5,7 +5,7 @@
*/
import { State } from 'mol-state';
import { PluginStateObjects as SO } from './state/objects';
import { PluginStateObject as SO } from './state/objects';
import { Camera } from 'mol-canvas3d/camera';
import { PluginBehavior } from './behavior';
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
// TODO actions that modify state and can be "applied" to certain state objects.
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { StateObject, Transformer } from 'mol-state';
import { Representation } from 'mol-repr';
import { PluginBehavior } from 'mol-plugin/behavior/behavior';
export type TypeClass = 'root' | 'data' | 'prop'
export namespace PluginStateObject {
export type Any = StateObject<any, TypeInfo>
export type TypeClass = 'Root' | 'Group' | 'Data' | 'Object' | 'Representation3D' | 'Behavior'
export interface TypeInfo { name: string, typeClass: TypeClass }
export const Create = StateObject.factory<TypeInfo>();
export function isRepresentation3D(o?: Any): o is StateObject<Representation.Any, TypeInfo> {
return !!o && o.type.typeClass === 'Representation3D';
}
export function isBehavior(o?: Any): o is StateObject<PluginBehavior, TypeInfo> {
return !!o && o.type.typeClass === 'Behavior';
}
export function CreateRepresentation3D<T extends Representation.Any>(type: { name: string }) {
return Create<T>({ ...type, typeClass: 'Representation3D' })
}
export function CreateBehavior<T extends PluginBehavior>(type: { name: string }) {
return Create<T>({ ...type, typeClass: 'Behavior' })
}
}
export namespace PluginStateTransform {
export const Create = Transformer.factory('ms-plugin');
}
\ No newline at end of file
......@@ -4,44 +4,70 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { PluginStateObject } from './base';
import { CifFile } from 'mol-io/reader/cif';
import { Model as _Model, Structure as _Structure } from 'mol-model/structure'
import { Model as _Model, Structure as _Structure } from 'mol-model/structure';
import { VolumeData } from 'mol-model/volume';
import { PluginBehavior } from 'mol-plugin/behavior/behavior';
import { Representation } from 'mol-repr';
import { StructureRepresentation } from 'mol-repr/structure/index';
import { VolumeRepresentation } from 'mol-repr/volume';
import { VolumeData } from 'mol-model/volume';
import { StateObject, Transformer } from 'mol-state';
export type TypeClass = 'root' | 'data' | 'prop'
export namespace PluginStateObject {
export type Any = StateObject<any, TypeInfo>
export type TypeClass = 'Root' | 'Group' | 'Data' | 'Object' | 'Representation3D' | 'Behavior'
export interface TypeInfo { name: string, typeClass: TypeClass }
export const Create = StateObject.factory<TypeInfo>();
export function isRepresentation3D(o?: Any): o is StateObject<Representation.Any, TypeInfo> {
return !!o && o.type.typeClass === 'Representation3D';
}
const _create = PluginStateObject.Create, _createRepr3D = PluginStateObject.CreateRepresentation3D
export function isBehavior(o?: Any): o is StateObject<PluginBehavior, TypeInfo> {
return !!o && o.type.typeClass === 'Behavior';
}
namespace PluginStateObjects {
export class Root extends _create({ name: 'Root', typeClass: 'Root' }) { }
export function CreateRepresentation3D<T extends Representation.Any>(type: { name: string }) {
return Create<T>({ ...type, typeClass: 'Representation3D' })
}
export class Group extends _create({ name: 'Group', typeClass: 'Group' }) { }
export function CreateBehavior<T extends PluginBehavior>(type: { name: string }) {
return Create<T>({ ...type, typeClass: 'Behavior' })
}
export class Root extends Create({ name: 'Root', typeClass: 'Root' }) { }
export class Group extends Create({ name: 'Group', typeClass: 'Group' }) { }
export namespace Data {
export class String extends _create<string>({ name: 'String Data', typeClass: 'Data', }) { }
export class Binary extends _create<Uint8Array>({ name: 'Binary Data', typeClass: 'Data' }) { }
export class Json extends _create<any>({ name: 'JSON Data', typeClass: 'Data' }) { }
export class Cif extends _create<CifFile>({ name: 'CIF File', typeClass: 'Data' }) { }
export class String extends Create<string>({ name: 'String Data', typeClass: 'Data', }) { }
export class Binary extends Create<Uint8Array>({ name: 'Binary Data', typeClass: 'Data' }) { }
export class Json extends Create<any>({ name: 'JSON Data', typeClass: 'Data' }) { }
export class Cif extends Create<CifFile>({ name: 'CIF File', typeClass: 'Data' }) { }
// TODO
// export class MultipleRaw extends _create<{
// export class MultipleRaw extends Create<{
// [key: string]: { type: 'String' | 'Binary', data: string | Uint8Array }
// }>({ name: 'Data', typeClass: 'Data', shortName: 'MD', description: 'Multiple Keyed Data.' }) { }
}
export namespace Molecule {
export class Trajectory extends _create<ReadonlyArray<_Model>>({ name: 'Trajectory', typeClass: 'Object' }) { }
export class Model extends _create<_Model>({ name: 'Model', typeClass: 'Object' }) { }
export class Structure extends _create<_Structure>({ name: 'Structure', typeClass: 'Object' }) { }
export class Representation3D extends _createRepr3D<StructureRepresentation<any>>({ name: 'Structure 3D' }) { }
export class Trajectory extends Create<ReadonlyArray<_Model>>({ name: 'Trajectory', typeClass: 'Object' }) { }
export class Model extends Create<_Model>({ name: 'Model', typeClass: 'Object' }) { }
export class Structure extends Create<_Structure>({ name: 'Structure', typeClass: 'Object' }) { }
export class Representation3D extends CreateRepresentation3D<StructureRepresentation<any>>({ name: 'Structure 3D' }) { }
}
export namespace Volume {
export class Data extends _create<VolumeData>({ name: 'Volume Data', typeClass: 'Object' }) { }
export class Representation3D extends _createRepr3D<VolumeRepresentation<any>>({ name: 'Volume 3D' }) { }
export class Data extends Create<VolumeData>({ name: 'Volume Data', typeClass: 'Object' }) { }
export class Representation3D extends CreateRepresentation3D<VolumeRepresentation<any>>({ name: 'Volume 3D' }) { }
}
}
export { PluginStateObjects }
\ No newline at end of file
export namespace PluginStateTransform {
export const Create = Transformer.factory('ms-plugin');
}
\ No newline at end of file
......@@ -4,8 +4,8 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { PluginStateTransform } from '../base';
import { PluginStateObjects as SO } from '../objects';
import { PluginStateTransform } from '../objects';
import { PluginStateObject as SO } from '../objects';
import { Task } from 'mol-task';
import CIF from 'mol-io/reader/cif'
import { PluginContext } from 'mol-plugin/context';
......
......@@ -4,8 +4,8 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { PluginStateTransform } from '../base';
import { PluginStateObjects as SO } from '../objects';
import { PluginStateTransform } from '../objects';
import { PluginStateObject as SO } from '../objects';
import { Task } from 'mol-task';
import { Model, Format, Structure, ModelSymmetry, StructureSymmetry, QueryContext, StructureSelection } from 'mol-model/structure';
import { ParamDefinition as PD } from 'mol-util/param-definition';
......
......@@ -6,9 +6,8 @@
import { Transformer } from 'mol-state';
import { Task } from 'mol-task';
import { PluginStateTransform } from '../base';
import { PluginStateObjects as SO } from '../objects';
//import { CartoonRepresentation, DefaultCartoonProps } from 'mol-repr/structure/representation/cartoon';
import { PluginStateTransform } from '../objects';
import { PluginStateObject as SO } from '../objects';
import { BallAndStickRepresentation, DefaultBallAndStickProps } from 'mol-repr/structure/representation/ball-and-stick';
import { PluginContext } from 'mol-plugin/context';
......
......@@ -6,7 +6,7 @@
import * as React from 'react';
import { PluginContext } from '../context';
import { PluginStateObject } from 'mol-plugin/state/base';
import { PluginStateObject } from 'mol-plugin/state/objects';
import { State } from 'mol-state'
import { PluginCommands } from 'mol-plugin/command';
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Task } from 'mol-task';
import { UUID } from 'mol-util';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import { StateObject, StateObjectCell } from './object';
import { State } from './state';
import { Transformer } from './transformer';
export { StateAction };
interface StateAction<A extends StateObject = StateObject, T = any, P = unknown> {
readonly id: UUID,
readonly definition: StateAction.Definition<A, T, P>
}
namespace StateAction {
export type Id = string & { '@type': 'transformer-id' }
export type Params<T extends StateAction<any, any, any>> = T extends StateAction<any, any, infer P> ? P : unknown;
export type ReType<T extends StateAction<any, any, any>> = T extends StateAction<any, infer T, any> ? T : unknown;
export type ControlsFor<Props> = { [P in keyof Props]?: PD.Any }
export interface ApplyParams<A extends StateObject = StateObject, P = unknown> {
cell: StateObjectCell,
a: A,
state: State,
params: P
}
export interface Definition<A extends StateObject = StateObject, T = any, P = unknown> {
readonly from: StateObject.Ctor[],
readonly display?: { readonly name: string, readonly description?: string },
/**
* Apply an action that modifies the State specified in Params.
*/
apply(params: ApplyParams<A, P>, globalCtx: unknown): T | Task<T>,
readonly params?: Transformer<A, any, P>['definition']['params'],
/** Test if the transform can be applied to a given node */
isApplicable?(a: A, globalCtx: unknown): boolean
}
export function create<A extends StateObject, T, P>(definition: Definition<A, T, P>): StateAction<A, T, P> {
return { id: UUID.create(), definition };
}
export function fromTransformer<T extends Transformer>(transformer: T) {
const def = transformer.definition;
return create<Transformer.From<T>, void, Transformer.Params<T>>({
from: def.from,
display: def.display,
params: def.params as Transformer<Transformer.From<T>, any, Transformer.Params<T>>['definition']['params'],
apply({ cell, state, params }) {
const tree = state.build().to(cell.transform.ref).apply(transformer, params);
return state.update(tree);
}
})
}
}
\ No newline at end of file
......@@ -13,6 +13,7 @@ import { RuntimeContext, Task } from 'mol-task';
import { StateSelection } from './state/selection';
import { RxEventHelper } from 'mol-util/rx-event-helper';
import { StateTreeBuilder } from './tree/builder';
import { StateAction } from './action';
export { State }
......@@ -80,6 +81,10 @@ class State {
return StateSelection.select(selector(StateSelection.Generators), this)
}
apply(action: StateAction, ref: Transform.Ref) {
}
update(tree: StateTree | StateTreeBuilder): Task<void> {
// TODO: support cell state
const _tree = StateTreeBuilder.is(tree) ? tree.getTree() : tree;
......@@ -254,19 +259,6 @@ function doError(ctx: UpdateContext, ref: Ref, errorText: string) {
}
}
function findAncestor(tree: StateTree, cells: State.Cells, root: Ref, types: { type: StateObject.Type }[]): StateObjectCell | undefined {
let current = tree.nodes.get(root)!;
while (true) {
current = tree.nodes.get(current.parent)!;
const cell = cells.get(current.ref)!;
if (!cell.obj) return void 0;
for (const t of types) if (cell.obj.type === t.type) return cells.get(current.ref)!;
if (current.ref === Transform.RootRef) {
return void 0;
}
}
}
async function updateSubtree(ctx: UpdateContext, root: Ref) {
setCellStatus(ctx, root, 'processing');
......@@ -296,7 +288,7 @@ async function updateSubtree(ctx: UpdateContext, root: Ref) {
async function updateNode(ctx: UpdateContext, currentRef: Ref) {
const { oldTree, tree } = ctx;
const transform = tree.nodes.get(currentRef);
const parentCell = findAncestor(tree, ctx.cells, currentRef, transform.transformer.definition.from);
const parentCell = StateSelection.findAncestorOfType(tree, ctx.cells, currentRef, transform.transformer.definition.from);
if (!parentCell) {
throw new Error(`No suitable parent found for '${currentRef}'`);
......
......@@ -123,6 +123,7 @@ namespace StateSelection {
const set = new Set<string>();
const ret: StateObjectCell[] = [];
for (const n of q(state)) {
if (!n) continue;
if (!set.has(n.transform.ref)) {
set.add(n.transform.ref);
ret.push(n);
......@@ -169,22 +170,24 @@ namespace StateSelection {
export function ofType(b: Selector, t: StateObject.Type) { return filter(b, n => n.obj ? n.obj.type === t : false); }
registerModifier('ancestorOfType', ancestorOfType);
export function ancestorOfType(b: Selector, types: StateObject.Ctor[]) { return unique(mapEntity(b, (n, s) => findAncestorOfType(s, n.transform.ref, types))); }
export function ancestorOfType(b: Selector, types: StateObject.Ctor[]) { return unique(mapEntity(b, (n, s) => findAncestorOfType(s.tree, s.cells, n.transform.ref, types))); }
registerModifier('parent', parent);
export function parent(b: Selector) { return unique(mapEntity(b, (n, s) => s.cells.get(s.tree.nodes.get(n.transform.ref)!.parent))); }
function findAncestorOfType({ tree, cells }: State, root: string, types: StateObject.Ctor[]): StateObjectCell | undefined {
export function findAncestorOfType(tree: StateTree, cells: State.Cells, root: Transform.Ref, types: StateObject.Ctor[]): StateObjectCell | undefined {
let current = tree.nodes.get(root)!, len = types.length;
while (true) {
current = tree.nodes.get(current.parent)!;
if (current.ref === Transform.RootRef) {
return cells.get(Transform.RootRef);
}
const obj = cells.get(current.ref)!.obj!;
const cell = cells.get(current.ref)!;
if (!cell.obj) return void 0;
const obj = cell.obj;
for (let i = 0; i < len; i++) {
if (obj.type === types[i].type) return cells.get(current.ref);
}
if (current.ref === Transform.RootRef) {
return void 0;
}
}
}
}
......
......@@ -8,9 +8,11 @@ import { Task } from 'mol-task';
import { StateObject } from './object';
import { Transform } from './transform';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import { StateAction } from './action';
export interface Transformer<A extends StateObject = StateObject, B extends StateObject = StateObject, P = unknown> {
apply(parent: Transform.Ref, params?: P, props?: Partial<Transform.Options>): Transform<A, B, P>,
toAction(): StateAction<A, void, P>,
readonly namespace: string,
readonly id: Transformer.Id,
readonly definition: Transformer.Definition<A, B, P>
......@@ -19,6 +21,7 @@ export interface Transformer<A extends StateObject = StateObject, B extends Stat
export namespace Transformer {
export type Id = string & { '@type': 'transformer-id' }
export type Params<T extends Transformer<any, any, any>> = T extends Transformer<any, any, infer P> ? P : unknown;
export type From<T extends Transformer<any, any, any>> = T extends Transformer<infer A, any, any> ? A : unknown;
export type To<T extends Transformer<any, any, any>> = T extends Transformer<any, infer B, any> ? B : unknown;
export type ControlsFor<Props> = { [P in keyof Props]?: PD.Any }
......@@ -114,7 +117,8 @@ export namespace Transformer {
}
const t: Transformer<A, B, P> = {
apply(parent, params, props) { return Transform.create<A, B, P>(parent, t as any, params, props); },
apply(parent, params, props) { return Transform.create<A, B, P>(parent, t, params, props); },
toAction() { return StateAction.fromTransformer(t); },
namespace,
id,
definition
......
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