diff --git a/src/mol-plugin/behavior.ts b/src/mol-plugin/behavior.ts index db04070ecb343ec2c46b18dff1d545a6c8569b9b..9758f66293cb062d18034bfac2508c3d6bca78a7 100644 --- a/src/mol-plugin/behavior.ts +++ b/src/mol-plugin/behavior.ts @@ -5,8 +5,8 @@ */ export * from './behavior/behavior' -import * as Data from './behavior/data' -import * as Representation from './behavior/representation' +import * as Data from './behavior/built-in/data' +import * as Representation from './behavior/built-in/representation' export const PluginBehaviors = { Data, diff --git a/src/mol-plugin/behavior/behavior.ts b/src/mol-plugin/behavior/behavior.ts index ac667eee751a42cf025f9f62e41400a91f27343c..1064ac2ad5b21afa14c16ec4444543a5e198becc 100644 --- a/src/mol-plugin/behavior/behavior.ts +++ b/src/mol-plugin/behavior/behavior.ts @@ -4,8 +4,7 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { PluginStateTransform } from '../state/base'; -import { PluginStateObjects as SO } from '../state/objects'; +import { PluginStateTransform, PluginStateObject } from '../state/base'; import { Transformer } from 'mol-state'; import { Task } from 'mol-task'; import { PluginContext } from 'mol-plugin/context'; @@ -23,6 +22,9 @@ interface PluginBehavior<P = unknown> { } namespace PluginBehavior { + export class Root extends PluginStateObject.Create({ name: 'Root', shortName: 'R', typeClass: 'Root', description: 'Where everything begins.' }) { } + export class Behavior extends PluginStateObject.CreateBehavior<PluginBehavior>({ name: 'Behavior', shortName: 'B', description: 'Modifies plugin functionality.' }) { } + export interface Ctor<P = undefined> { new(ctx: PluginContext, params?: P): PluginBehavior<P> } export interface CreateParams<P> { @@ -30,19 +32,19 @@ namespace PluginBehavior { ctor: Ctor<P>, label?: (params: P) => { label: string, description?: string }, display: { name: string, description?: string }, - params?: Transformer.Definition<SO.BehaviorRoot, SO.Behavior, P>['params'] + params?: Transformer.Definition<Root, Behavior, P>['params'] } export function create<P>(params: CreateParams<P>) { - return PluginStateTransform.Create<SO.BehaviorRoot, SO.Behavior, P>({ + return PluginStateTransform.Create<Root, Behavior, P>({ name: params.name, display: params.display, - from: [SO.BehaviorRoot], - to: [SO.Behavior], + from: [Root], + to: [Behavior], params: params.params, apply({ params: p }, ctx: PluginContext) { const label = params.label ? params.label(p) : { label: params.display.name, description: params.display.description }; - return new SO.Behavior(label, new params.ctor(ctx, p)); + return new Behavior(label, new params.ctor(ctx, p)); }, update({ b, newParams }) { return Task.create('Update Behavior', async () => { diff --git a/src/mol-plugin/behavior/camera.ts b/src/mol-plugin/behavior/built-in/camera.ts similarity index 100% rename from src/mol-plugin/behavior/camera.ts rename to src/mol-plugin/behavior/built-in/camera.ts diff --git a/src/mol-plugin/behavior/data.ts b/src/mol-plugin/behavior/built-in/data.ts similarity index 96% rename from src/mol-plugin/behavior/data.ts rename to src/mol-plugin/behavior/built-in/data.ts index 730e3c2176e6e074b09188ca950659e5f5352f10..d9e6a6b745ee2ffec6d8996deedf4be2d548309d 100644 --- a/src/mol-plugin/behavior/data.ts +++ b/src/mol-plugin/behavior/built-in/data.ts @@ -4,7 +4,7 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { PluginBehavior } from './behavior'; +import { PluginBehavior } from '../behavior'; import { PluginCommands } from 'mol-plugin/command'; import { StateTree } from 'mol-state'; diff --git a/src/mol-plugin/behavior/representation.ts b/src/mol-plugin/behavior/built-in/representation.ts similarity index 73% rename from src/mol-plugin/behavior/representation.ts rename to src/mol-plugin/behavior/built-in/representation.ts index f2eac5aa9da549e3c1f483dca98e6cb2ca458313..5246082ca95501172406d39b05f52da76458a27c 100644 --- a/src/mol-plugin/behavior/representation.ts +++ b/src/mol-plugin/behavior/built-in/representation.ts @@ -4,38 +4,36 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { PluginBehavior } from './behavior'; -import { PluginStateObjects as SO } from '../state/objects'; +import { PluginBehavior } from '../behavior'; +import { PluginStateObject as SO } from '../../state/base'; class _AddRepresentationToCanvas extends PluginBehavior.Handler { register(): void { this.subscribeObservable(this.ctx.events.state.data.object.created, o => { - if (!SO.StructureRepresentation3D.is(o.obj)) return; + if (!SO.isRepresentation3D(o.obj)) return; this.ctx.canvas3d.add(o.obj.data); this.ctx.canvas3d.requestDraw(true); }); this.subscribeObservable(this.ctx.events.state.data.object.updated, o => { const oo = o.obj; - if (!SO.StructureRepresentation3D.is(oo)) return; + if (!SO.isRepresentation3D(oo)) return; this.ctx.canvas3d.add(oo.data); this.ctx.canvas3d.requestDraw(true); }); this.subscribeObservable(this.ctx.events.state.data.object.removed, o => { const oo = o.obj; - console.log('removed', o.ref, oo && oo.type); - if (!SO.StructureRepresentation3D.is(oo)) return; + if (!SO.isRepresentation3D(oo)) return; this.ctx.canvas3d.remove(oo.data); - console.log('removed from canvas', o.ref); this.ctx.canvas3d.requestDraw(true); oo.data.destroy(); }); this.subscribeObservable(this.ctx.events.state.data.object.replaced, o => { - if (o.oldObj && SO.StructureRepresentation3D.is(o.oldObj)) { + if (o.oldObj && SO.isRepresentation3D(o.oldObj)) { this.ctx.canvas3d.remove(o.oldObj.data); this.ctx.canvas3d.requestDraw(true); o.oldObj.data.destroy(); } - if (o.newObj && SO.StructureRepresentation3D.is(o.newObj)) { + if (o.newObj && SO.isRepresentation3D(o.newObj)) { this.ctx.canvas3d.add(o.newObj.data); this.ctx.canvas3d.requestDraw(true); } diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts index b519cc8d77643008d90063a49f9e73a9c90c5326..d32cb9bf97c4885c7d56bfda220ab3590e79ecb5 100644 --- a/src/mol-plugin/context.ts +++ b/src/mol-plugin/context.ts @@ -7,6 +7,7 @@ import { StateTree, StateSelection, Transformer, Transform } 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 { RxEventHelper } from 'mol-util/rx-event-helper'; import { PluginState } from './state'; @@ -77,10 +78,10 @@ export class PluginContext { async _test_initBehaviours() { const tree = StateTree.build(this.state.behavior.tree) - .toRoot().apply(PluginBehaviors.Data.SetCurrentObject) - .and().toRoot().apply(PluginBehaviors.Data.Update) - .and().toRoot().apply(PluginBehaviors.Data.RemoveObject) - .and().toRoot().apply(PluginBehaviors.Representation.AddRepresentationToCanvas) + .toRoot().apply(PluginBehaviors.Data.SetCurrentObject, { ref: PluginBehaviors.Data.SetCurrentObject.id }) + .and().toRoot().apply(PluginBehaviors.Data.Update, { ref: PluginBehaviors.Data.Update.id }) + .and().toRoot().apply(PluginBehaviors.Data.RemoveObject, { ref: PluginBehaviors.Data.RemoveObject.id }) + .and().toRoot().apply(PluginBehaviors.Representation.AddRepresentationToCanvas, { ref: PluginBehaviors.Representation.AddRepresentationToCanvas.id }) .getTree(); await this.state.updateBehaviour(tree); @@ -121,32 +122,32 @@ export class PluginContext { private initEvents() { merge(this.events.state.data.object.created, this.events.state.behavior.object.created).subscribe(o => { console.log('creating', o.obj.type); - if (!SO.Behavior.is(o.obj)) return; + if (!PSO.isBehavior(o.obj)) return; o.obj.data.register(); }); merge(this.events.state.data.object.removed, this.events.state.behavior.object.removed).subscribe(o => { - if (!SO.Behavior.is(o.obj)) return; + if (!PSO.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 && SO.Behavior.is(o.oldObj)) o.oldObj.data.unregister(); - if (o.newObj && SO.Behavior.is(o.newObj)) o.newObj.data.register(); + if (o.oldObj && PSO.isBehavior(o.oldObj)) o.oldObj.data.unregister(); + if (o.newObj && PSO.isBehavior(o.newObj)) o.newObj.data.register(); }); } _test_centerView() { - const sel = StateSelection.select(StateSelection.root().subtree().ofType(SO.Structure.type), this.state.data); + const sel = StateSelection.select(StateSelection.root().subtree().ofType(SO.Molecule.Structure.type), this.state.data); if (!sel.length) return; - const center = (sel[0].obj! as SO.Structure).data.boundary.sphere.center; + const center = (sel[0].obj! as SO.Molecule.Structure).data.boundary.sphere.center; this.canvas3d.camera.setState({ target: center }); this.canvas3d.requestDraw(true); } _test_nextModel() { - const models = StateSelection.select('models', this.state.data)[0].obj as SO.Models; + const models = StateSelection.select('models', this.state.data)[0].obj as SO.Molecule.Models; const idx = (this.state.data.tree.getValue('structure')!.params as Transformer.Params<typeof StateTransforms.Model.CreateStructureFromModel>).modelIndex; const newTree = StateTree.updateParams(this.state.data.tree, 'structure', { modelIndex: (idx + 1) % models.data.length }); return this.state.updateData(newTree); diff --git a/src/mol-plugin/state.ts b/src/mol-plugin/state.ts index 47981d07192234d17aa7bd13b69a23e739bd5037..80cc88b03bfe4b6821fbb4b7d8e642c2528835ad 100644 --- a/src/mol-plugin/state.ts +++ b/src/mol-plugin/state.ts @@ -7,6 +7,7 @@ import { State, StateTree } from 'mol-state'; import { PluginStateObjects as SO } from './state/objects'; import { Camera } from 'mol-canvas3d/camera'; +import { PluginBehavior } from './behavior'; export { PluginState } @@ -28,15 +29,7 @@ class PluginState { await this.behavior.setSnapshot(snapshot.behaviour); await this.data.setSnapshot(snapshot.data); this.plugin.canvas3d.camera.setState(snapshot.canvas3d.camera); - - // TODO: handle camera - // console.log({ old: { ...this.plugin.canvas3d.camera }, new: snapshot.canvas3d.camera }); - // CombinedCamera.copy(snapshot.canvas3d.camera, this.plugin.canvas3d.camera); - // CombinedCamera.update(this.plugin.canvas3d.camera); - // this.plugin.canvas3d.center - // console.log({ copied: { ...this.plugin.canvas3d.camera } }); this.plugin.canvas3d.requestDraw(true); - // console.log('updated camera'); } updateData(tree: StateTree) { @@ -52,8 +45,8 @@ class PluginState { } constructor(private plugin: import('./context').PluginContext) { - this.data = State.create(new SO.DataRoot({ label: 'Root' }, { }), { globalContext: plugin }); - this.behavior = State.create(new SO.BehaviorRoot({ label: 'Root' }, { }), { globalContext: plugin }); + this.data = State.create(new SO.Root({ label: 'Root' }, { }), { globalContext: plugin }); + this.behavior = State.create(new PluginBehavior.Root({ label: 'Root' }, { }), { globalContext: plugin }); } } diff --git a/src/mol-plugin/state/base.ts b/src/mol-plugin/state/base.ts index 5b7db3fc0583f5f62c809f6b19e1159632680ec1..05e2f627806696318bad63aef0b969f930260439 100644 --- a/src/mol-plugin/state/base.ts +++ b/src/mol-plugin/state/base.ts @@ -5,15 +5,33 @@ */ 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 TypeClass = 'Root' | 'Group' | 'Data' | 'Object' | 'Representation' | 'Behavior' + 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 isBehavior(o?: StateObject): o is StateObject<Props, PluginBehavior> { + return !!o && (o.type.info as TypeInfo).typeClass === 'Behavior'; + } + + export function CreateRepresentation3D<T extends Representation.Any>(type: { name: string, shortName: string, description: string }) { + return Create<T>({ ...type, typeClass: 'Representation3D' }) + } + + export function CreateBehavior<T extends PluginBehavior>(type: { name: string, shortName: string, description: string }) { + return Create<T>({ ...type, typeClass: 'Behavior' }) + } } export namespace PluginStateTransform { diff --git a/src/mol-plugin/state/objects.ts b/src/mol-plugin/state/objects.ts index cb2cf337c0014d2a58f957595bce61982c4b9b3b..17784d5a75ccbc606a1958878c7fe32580451ff2 100644 --- a/src/mol-plugin/state/objects.ts +++ b/src/mol-plugin/state/objects.ts @@ -8,17 +8,16 @@ import { PluginStateObject } from './base'; import { CifFile } from 'mol-io/reader/cif'; import { Model as _Model, Structure as _Structure } from 'mol-model/structure' import { StructureRepresentation } from 'mol-repr/structure/index'; +import { VolumeRepresentation } from 'mol-repr/volume'; +import { VolumeData } from 'mol-model/volume'; -const _create = PluginStateObject.Create +const _create = PluginStateObject.Create, _createRepr3D = PluginStateObject.CreateRepresentation3D namespace PluginStateObjects { - export class DataRoot extends _create({ name: 'Root', shortName: 'R', typeClass: 'Root', description: 'Where everything begins.' }) { } - export class BehaviorRoot extends _create({ name: 'Root', shortName: 'R', typeClass: 'Root', description: 'Where everything begins.' }) { } + export class Root extends _create({ name: 'Root', shortName: 'R', typeClass: 'Root', description: 'Where everything begins.' }) { } export class Group extends _create({ name: 'Group', shortName: 'G', typeClass: 'Group', description: 'A group on entities.' }) { } - export class Behavior extends _create<import('../behavior').PluginBehavior>({ name: 'Behavior', shortName: 'B', typeClass: 'Behavior', description: 'Modifies plugin functionality.' }) { } - export namespace Data { export class String extends _create<string>({ name: 'String Data', typeClass: 'Data', shortName: 'S_D', description: 'A string.' }) { } export class Binary extends _create<Uint8Array>({ name: 'Binary Data', typeClass: 'Data', shortName: 'B_D', description: 'A binary blob.' }) { } @@ -31,11 +30,17 @@ namespace PluginStateObjects { // }>({ name: 'Data', typeClass: 'Data', shortName: 'MD', description: 'Multiple Keyed Data.' }) { } } - export class Models extends _create<ReadonlyArray<_Model>>({ name: 'Molecule Model', typeClass: 'Object', shortName: 'M_M', description: 'A model of a molecule.' }) { } - export class Structure extends _create<_Structure>({ name: 'Molecule Structure', typeClass: 'Object', shortName: 'M_S', description: 'A structure of a molecule.' }) { } + export namespace Molecule { + export class Models extends _create<ReadonlyArray<_Model>>({ name: 'Molecule Model', typeClass: 'Object', shortName: 'M_M', description: 'A model of a molecule.' }) { } + export class Structure extends _create<_Structure>({ name: 'Molecule Structure', typeClass: 'Object', shortName: 'M_S', description: 'A structure of a molecule.' }) { } + export class Representation3D extends _createRepr3D<StructureRepresentation<any>>({ name: 'Molecule Structure 3D Representation', shortName: 'S_R', description: 'A 3D representation of a molecular structure.' }) { } + } + export namespace Volume { + export class Data extends _create<VolumeData>({ name: 'Volume Data', typeClass: 'Object', shortName: 'V_D', description: 'Volume Data.' }) { } + export class Representation3D extends _createRepr3D<VolumeRepresentation<any>>({ name: 'Volume 3D Representation', shortName: 'V_R', description: 'A 3D representation of volumetric data.' }) { } + } - export class StructureRepresentation3D extends _create<StructureRepresentation<any>>({ name: 'Molecule Structure Representation', typeClass: 'Representation', shortName: 'S_R', description: 'A representation of a molecular structure.' }) { } } export { PluginStateObjects } \ No newline at end of file diff --git a/src/mol-plugin/state/transforms/data.ts b/src/mol-plugin/state/transforms/data.ts index 67899a9c666663370ce38be4717ef5d37428776e..52344c2d90b67dfc66c2d505667228db6ee0a628 100644 --- a/src/mol-plugin/state/transforms/data.ts +++ b/src/mol-plugin/state/transforms/data.ts @@ -13,13 +13,13 @@ import { ParamDefinition as PD } from 'mol-util/param-definition'; export { Download } namespace Download { export interface Params { url: string, isBinary?: boolean, label?: string } } -const Download = PluginStateTransform.Create<SO.DataRoot, SO.Data.String | SO.Data.Binary, Download.Params>({ +const Download = PluginStateTransform.Create<SO.Root, SO.Data.String | SO.Data.Binary, Download.Params>({ name: 'download', display: { name: 'Download', description: 'Download string or binary data from the specified URL' }, - from: [SO.DataRoot], + from: [SO.Root], to: [SO.Data.String, SO.Data.Binary], params: { default: () => ({ diff --git a/src/mol-plugin/state/transforms/model.ts b/src/mol-plugin/state/transforms/model.ts index 1ebe478e28a9ffcd0dff29c587c5aac811bb401d..47cd012e089eb5a0a035d96147681c874cfa020a 100644 --- a/src/mol-plugin/state/transforms/model.ts +++ b/src/mol-plugin/state/transforms/model.ts @@ -15,14 +15,14 @@ import { Mat4 } from 'mol-math/linear-algebra'; export { ParseModelsFromMmCif } namespace ParseModelsFromMmCif { export interface Params { blockHeader?: string } } -const ParseModelsFromMmCif = PluginStateTransform.Create<SO.Data.Cif, SO.Models, ParseModelsFromMmCif.Params>({ +const ParseModelsFromMmCif = PluginStateTransform.Create<SO.Data.Cif, SO.Molecule.Models, ParseModelsFromMmCif.Params>({ name: 'parse-models-from-mmcif', display: { name: 'Models from mmCIF', description: 'Identify and create all separate models in the specified CIF data block' }, from: [SO.Data.Cif], - to: [SO.Models], + to: [SO.Molecule.Models], params: { default: a => ({ blockHeader: a.data.blocks[0].header }), controls(a) { @@ -41,21 +41,21 @@ const ParseModelsFromMmCif = PluginStateTransform.Create<SO.Data.Cif, SO.Models, const models = await Model.create(Format.mmCIF(block)).runInContext(ctx); if (models.length === 0) throw new Error('No models found.'); const label = models.length === 1 ? `${models[0].label}` : `${models[0].label} (${models.length} models)`; - return new SO.Models({ label }, models); + return new SO.Molecule.Models({ label }, models); }); } }); export { CreateStructureFromModel } namespace CreateStructureFromModel { export interface Params { modelIndex: number, transform3d?: Mat4 } } -const CreateStructureFromModel = PluginStateTransform.Create<SO.Models, SO.Structure, CreateStructureFromModel.Params>({ +const CreateStructureFromModel = PluginStateTransform.Create<SO.Molecule.Models, SO.Molecule.Structure, CreateStructureFromModel.Params>({ name: 'create-structure-from-model', display: { name: 'Structure from Model', description: 'Create a molecular structure from the specified model.' }, - from: [SO.Models], - to: [SO.Structure], + from: [SO.Molecule.Models], + to: [SO.Molecule.Structure], params: { default: () => ({ modelIndex: 0 }), controls: a => ({ modelIndex: PD.Range('Model Index', 'Model Index', 0, 0, Math.max(0, a.data.length - 1), 1) }) @@ -65,21 +65,21 @@ const CreateStructureFromModel = PluginStateTransform.Create<SO.Models, SO.Struc if (params.modelIndex < 0 || params.modelIndex >= a.data.length) throw new Error(`Invalid modelIndex ${params.modelIndex}`); let s = Structure.ofModel(a.data[params.modelIndex]); if (params.transform3d) s = Structure.transform(s, params.transform3d); - return new SO.Structure({ label: `Model ${s.models[0].modelNum}`, description: s.elementCount === 1 ? '1 element' : `${s.elementCount} elements` }, s); + return new SO.Molecule.Structure({ label: `Model ${s.models[0].modelNum}`, description: s.elementCount === 1 ? '1 element' : `${s.elementCount} elements` }, s); } }); export { CreateStructureAssembly } namespace CreateStructureAssembly { export interface Params { /** if not specified, use the 1st */ id?: string } } -const CreateStructureAssembly = PluginStateTransform.Create<SO.Structure, SO.Structure, CreateStructureAssembly.Params>({ +const CreateStructureAssembly = PluginStateTransform.Create<SO.Molecule.Structure, SO.Molecule.Structure, CreateStructureAssembly.Params>({ name: 'create-structure-assembly', display: { name: 'Structure Assembly', description: 'Create a molecular structure assembly.' }, - from: [SO.Structure], - to: [SO.Structure], + from: [SO.Molecule.Structure], + to: [SO.Molecule.Structure], params: { default: () => ({ id: void 0 }), controls(a) { @@ -98,26 +98,26 @@ const CreateStructureAssembly = PluginStateTransform.Create<SO.Structure, SO.Str if (!asm) throw new Error(`Assembly '${id}' not found`); const s = await StructureSymmetry.buildAssembly(a.data, id!).runInContext(ctx); - return new SO.Structure({ label: `Assembly ${id}`, description: s.elementCount === 1 ? '1 element' : `${s.elementCount} elements` }, s); + return new SO.Molecule.Structure({ label: `Assembly ${id}`, description: s.elementCount === 1 ? '1 element' : `${s.elementCount} elements` }, s); }) } }); export { CreateStructureSelection } namespace CreateStructureSelection { export interface Params { query: Expression, label?: string } } -const CreateStructureSelection = PluginStateTransform.Create<SO.Structure, SO.Structure, CreateStructureSelection.Params>({ +const CreateStructureSelection = PluginStateTransform.Create<SO.Molecule.Structure, SO.Molecule.Structure, CreateStructureSelection.Params>({ name: 'create-structure-selection', display: { name: 'Structure Selection', description: 'Create a molecular structure from the specified model.' }, - from: [SO.Structure], - to: [SO.Structure], + from: [SO.Molecule.Structure], + to: [SO.Molecule.Structure], apply({ a, params }) { // TODO: use cache, add "update" const compiled = compile<StructureSelection>(params.query); const result = compiled(new QueryContext(a.data)); const s = StructureSelection.unionStructure(result); - return new SO.Structure({ label: `${params.label || 'Selection'}`, description: s.elementCount === 1 ? '1 element' : `${s.elementCount} elements` }, s); + return new SO.Molecule.Structure({ label: `${params.label || 'Selection'}`, description: s.elementCount === 1 ? '1 element' : `${s.elementCount} elements` }, s); } }); diff --git a/src/mol-plugin/state/transforms/visuals.ts b/src/mol-plugin/state/transforms/visuals.ts index 3dbb0a621f161399802b8184f55767f729aa3dc6..d85aa7faa42c058ec208e8444e2ba5d6afdcca22 100644 --- a/src/mol-plugin/state/transforms/visuals.ts +++ b/src/mol-plugin/state/transforms/visuals.ts @@ -14,16 +14,16 @@ import { PluginContext } from 'mol-plugin/context'; export { CreateStructureRepresentation } namespace CreateStructureRepresentation { export interface Params { } } -const CreateStructureRepresentation = PluginStateTransform.Create<SO.Structure, SO.StructureRepresentation3D, CreateStructureRepresentation.Params>({ +const CreateStructureRepresentation = PluginStateTransform.Create<SO.Molecule.Structure, SO.Molecule.Representation3D, CreateStructureRepresentation.Params>({ name: 'create-structure-representation', display: { name: 'Create 3D Representation' }, - from: [SO.Structure], - to: [SO.StructureRepresentation3D], + from: [SO.Molecule.Structure], + to: [SO.Molecule.Representation3D], apply({ a, params }, plugin: PluginContext) { return Task.create('Structure Representation', async ctx => { const repr = BallAndStickRepresentation(); // CartoonRepresentation(); await repr.createOrUpdate({ webgl: plugin.canvas3d.webgl }, DefaultBallAndStickProps, a.data).runInContext(ctx); - return new SO.StructureRepresentation3D({ label: 'Visual Repr.' }, repr); + return new SO.Molecule.Representation3D({ label: 'Visual Repr.' }, repr); }); }, update({ a, b }, plugin: PluginContext) { diff --git a/src/mol-plugin/ui/plugin.tsx b/src/mol-plugin/ui/plugin.tsx index 9e1f84a230f7bf680ae368c8c79093060663f897..111e510b9c6f261b6d43eda28d875eb6a7a0a16a 100644 --- a/src/mol-plugin/ui/plugin.tsx +++ b/src/mol-plugin/ui/plugin.tsx @@ -11,15 +11,18 @@ import { Viewport } from './viewport'; import { Controls, _test_CreateTransform } from './controls'; import { Transformer } from 'mol-state'; -// TODO: base object with subscribe helpers +// TODO: base object with subscribe helpers, separate behavior list etc export class Plugin extends React.Component<{ plugin: PluginContext }, { }> { render() { return <div style={{ position: 'absolute', width: '100%', height: '100%', fontFamily: 'monospace' }}> <div style={{ position: 'absolute', width: '350px', height: '100%', overflowY: 'scroll' }}> - <StateTree plugin={this.props.plugin} /> + <h3>Data</h3> + <StateTree plugin={this.props.plugin} state={this.props.plugin.state.data} /> <hr /> <_test_CurrentObject plugin={this.props.plugin} /> + <h3>Behavior</h3> + <StateTree plugin={this.props.plugin} state={this.props.plugin.state.behavior} /> </div> <div style={{ position: 'absolute', left: '350px', right: '250px', height: '100%' }}> <Viewport plugin={this.props.plugin} /> diff --git a/src/mol-plugin/ui/state-tree.tsx b/src/mol-plugin/ui/state-tree.tsx index f7dd468e2a24c82b03dc95b82d28dbfad96c379c..01c9509152f75d1995d9905a38acb8d0fd06d5c4 100644 --- a/src/mol-plugin/ui/state-tree.tsx +++ b/src/mol-plugin/ui/state-tree.tsx @@ -7,46 +7,49 @@ import * as React from 'react'; import { PluginContext } from '../context'; import { PluginStateObject } from 'mol-plugin/state/base'; -import { StateObject } from 'mol-state' +import { StateObject, State } from 'mol-state' import { PluginCommands } from 'mol-plugin/command'; -export class StateTree extends React.Component<{ plugin: PluginContext }, { }> { +export class StateTree extends React.Component<{ plugin: PluginContext, state: State }, { }> { componentDidMount() { // TODO: move to constructor? - this.props.plugin.events.state.data.updated.subscribe(() => this.forceUpdate()); + this.props.state.context.events.updated.subscribe(() => this.forceUpdate()); } render() { // const n = this.props.plugin.state.data.tree.nodes.get(this.props.plugin.state.data.tree.rootRef)!; - const n = this.props.plugin.state.data.tree.rootRef; + const n = this.props.state.tree.rootRef; return <div> - <StateTreeNode plugin={this.props.plugin} nodeRef={n} key={n} /> + <StateTreeNode plugin={this.props.plugin} state={this.props.state} nodeRef={n} key={n} /> { /* n.children.map(c => <StateTreeNode plugin={this.props.plugin} nodeRef={c!} key={c} />) */} </div>; } } -export class StateTreeNode extends React.Component<{ plugin: PluginContext, nodeRef: string }, { }> { +export class StateTreeNode extends React.Component<{ plugin: PluginContext, nodeRef: string, state: State }, { }> { render() { - const n = this.props.plugin.state.data.tree.nodes.get(this.props.nodeRef)!; - const obj = this.props.plugin.state.data.objects.get(this.props.nodeRef)!; + const n = this.props.state.tree.nodes.get(this.props.nodeRef)!; + const obj = this.props.state.objects.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.obj) { - return <div style={{ borderLeft: '1px solid black', paddingLeft: '5px' }}> - {StateObject.StateType[obj.state]} {obj.errorText} + return <div style={{ borderLeft: '1px solid black', paddingLeft: '7px' }}> + {remove} {StateObject.StateType[obj.state]} {obj.errorText} </div>; } const props = obj.obj!.props as PluginStateObject.Props; const type = obj.obj!.type.info as PluginStateObject.TypeInfo; - return <div style={{ borderLeft: '1px solid #999', paddingLeft: '7px' }}> - [<a href='#' onClick={e => { - e.preventDefault(); - PluginCommands.Data.RemoveObject.dispatch(this.props.plugin, { ref: this.props.nodeRef }); - }}>X</a>][<span title={type.description}>{ type.shortName }</span>] <a href='#' onClick={e => { + return <div style={{ borderLeft: '0px solid #999', paddingLeft: '0px' }}> + {remove}[<span title={type.description}>{ type.shortName }</span>] <a href='#' onClick={e => { e.preventDefault(); PluginCommands.Data.SetCurrentObject.dispatch(this.props.plugin, { ref: this.props.nodeRef }); }}>{props.label}</a> {props.description ? <small>{props.description}</small> : void 0} {n.children.size === 0 ? void 0 - : <div style={{ marginLeft: '3px' }}>{n.children.map(c => <StateTreeNode plugin={this.props.plugin} nodeRef={c!} key={c} />)}</div> + : <div style={{ marginLeft: '7px', paddingLeft: '3px', borderLeft: '1px solid #999' }}>{n.children.map(c => <StateTreeNode plugin={this.props.plugin} state={this.props.state} nodeRef={c!} key={c} />)}</div> } </div>; }