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

wip plugin

parent 1eb9d0ed
No related branches found
No related tags found
No related merge requests found
......@@ -10,12 +10,13 @@ import { StateTransforms } from './state/transforms';
import { PluginStateObjects as SO } from './state/objects';
import { RxEventHelper } from 'mol-util/rx-event-helper';
import { PluginState } from './state';
import { MolScriptBuilder } from 'mol-script/language/builder';
export class PluginContext {
private disposed = false;
private ev = RxEventHelper.create();
readonly state = new PluginState();
readonly state = new PluginState(this);
readonly events = {
stateUpdated: this.ev<undefined>()
......@@ -54,11 +55,25 @@ export class PluginContext {
_test_createState(url: string) {
const b = StateTree.build(this.state.data.tree);
const query = MolScriptBuilder.struct.generator.atomGroups({
// 'atom-test': MolScriptBuilder.core.rel.eq([
// MolScriptBuilder.struct.atomProperty.macromolecular.label_comp_id(),
// MolScriptBuilder.es('C')
// ]),
'residue-test': MolScriptBuilder.core.rel.eq([
MolScriptBuilder.struct.atomProperty.macromolecular.label_comp_id(),
'ALA'
])
});
const newTree = b.toRoot()
.apply(StateTransforms.Data.Download, { url })
.apply(StateTransforms.Data.ParseCif)
.apply(StateTransforms.Model.CreateModelsFromMmCif, {}, { ref: 'models' })
.apply(StateTransforms.Model.ParseModelsFromMmCif, {}, { ref: 'models' })
.apply(StateTransforms.Model.CreateStructureFromModel, { modelIndex: 0 }, { ref: 'structure' })
.apply(StateTransforms.Model.CreateStructureAssembly)
.apply(StateTransforms.Model.CreateStructureSelection, { query, label: 'ALA residues' })
.apply(StateTransforms.Visuals.CreateStructureRepresentation)
.getTree();
......@@ -81,7 +96,7 @@ export class PluginContext {
this.state.data.context.events.object.updated.subscribe(o => {
const oo = o.obj;
if (!SO.StructureRepresentation3D.is(oo)) return;
console.log('adding repr', oo.data.repr);
console.log('updating repr', oo.data.repr);
this.canvas3d.add(oo.data.repr);
this.canvas3d.requestDraw(true);
});
......
......@@ -10,7 +10,7 @@ import { PluginStateObjects as SO } from './state/objects';
export { PluginState }
class PluginState {
readonly data = State.create(new SO.Root({ label: 'Root' }, { }));
readonly data: State;
getSnapshot(): PluginState.Snapshot {
throw 'nyi';
......@@ -27,6 +27,10 @@ class PluginState {
dispose() {
this.data.dispose();
}
constructor(globalContext: unknown) {
this.data = State.create(new SO.Root({ label: 'Root' }, { }), { globalContext });
}
}
namespace PluginState {
......
......@@ -11,7 +11,7 @@ export type TypeClass = 'root' | 'data' | 'prop'
export namespace PluginStateObject {
export type TypeClass = 'Root' | 'Group' | 'Data' | 'Object' | 'Representation' | 'Behaviour'
export interface TypeInfo { name: string, shortName: string, description: string, typeClass: TypeClass }
export interface Props { label: string, desctiption?: string }
export interface Props { label: string, description?: string }
export const Create = StateObject.factory<TypeInfo, Props>();
}
......
......@@ -9,6 +9,7 @@ import { PluginStateObjects as SO } from '../objects';
import { Task } from 'mol-task';
import CIF from 'mol-io/reader/cif'
import { PluginContext } from 'mol-plugin/context';
import { ParamDefinition as PD } from 'mol-util/param-definition';
export { Download }
namespace Download { export interface Params { url: string, isBinary?: boolean, label?: string } }
......@@ -20,6 +21,12 @@ const Download = PluginStateTransform.Create<SO.Root, SO.Data.String | SO.Data.B
},
from: [SO.Root],
to: [SO.Data.String, SO.Data.Binary],
params: {
controls: () => ({
url: PD.Text('URL', 'Resource URL. Must be the same domain or support CORS.', ''),
isBinary: PD.Boolean('Binary', 'If true, download data as binary (string otherwise)', false)
})
},
apply({ params: p }, globalCtx: PluginContext) {
return Task.create('Download', async ctx => {
// TODO: track progress
......
......@@ -7,19 +7,31 @@
import { PluginStateTransform } from '../base';
import { PluginStateObjects as SO } from '../objects';
import { Task } from 'mol-task';
import { Model, Format, Structure } from 'mol-model/structure';
import { Model, Format, Structure, ModelSymmetry, StructureSymmetry, QueryContext, StructureSelection } from 'mol-model/structure';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import Expression from 'mol-script/language/expression';
import { compile } from 'mol-script/runtime/query/compiler';
export { CreateModelsFromMmCif }
namespace CreateModelsFromMmCif { export interface Params { blockHeader?: string } }
const CreateModelsFromMmCif = PluginStateTransform.Create<SO.Data.Cif, SO.Models, CreateModelsFromMmCif.Params>({
name: 'create-models-from-mmcif',
export { ParseModelsFromMmCif }
namespace ParseModelsFromMmCif { export interface Params { blockHeader?: string } }
const ParseModelsFromMmCif = PluginStateTransform.Create<SO.Data.Cif, SO.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],
params: { default: a => ({ blockHeader: a.data.blocks[0].header }) },
params: {
default: a => ({ blockHeader: a.data.blocks[0].header }),
controls(a) {
const { blocks } = a.data;
if (blocks.length === 0) return {};
return {
blockHeader: PD.Select('Header', 'Header of the block to parse', blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]))
};
}
},
apply({ a, params }) {
return Task.create('Parse mmCIF', async ctx => {
const header = params.blockHeader || a.data.blocks[0].header;
......@@ -36,18 +48,73 @@ const CreateModelsFromMmCif = PluginStateTransform.Create<SO.Data.Cif, SO.Models
export { CreateStructureFromModel }
namespace CreateStructureFromModel { export interface Params { modelIndex: number } }
const CreateStructureFromModel = PluginStateTransform.Create<SO.Models, SO.Structure, CreateStructureFromModel.Params>({
name: 'structure-from-model',
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],
params: { default: () => ({ modelIndex: 0 }) },
params: {
default: () => ({ modelIndex: 0 }),
controls: a => ({ modelIndex: PD.Range('Model Index', 'Model Index', 0, 0, Math.max(0, a.data.length - 1), 1) })
},
apply({ a, params }) {
if (params.modelIndex < 0 || params.modelIndex >= a.data.length) throw new Error(`Invalid modelIndex ${params.modelIndex}`);
// TODO: make Structure.ofModel async?
const s = Structure.ofModel(a.data[params.modelIndex]);
return new SO.Structure({ label: `${a.data[params.modelIndex].label} (model ${s.models[0].modelNum})`, desctiption: s.elementCount === 1 ? '1 element' : `${s.elementCount} elements` }, s);
return new SO.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>({
name: 'create-structure-assembly',
display: {
name: 'Structure Assembly',
description: 'Create a molecular structure assembly.'
},
from: [SO.Structure],
to: [SO.Structure],
params: {
default: () => ({ id: void 0 }),
controls(a) {
const { model } = a.data;
const ids = model.symmetry.assemblies.map(a => [a.id, a.id] as [string, string]);
return { id: PD.Select('Asm Id', 'Assembly Id', ids.length ? ids[0][0] : '', ids) };
}
},
isApplicable: a => a.data.models.length === 1 && a.data.model.symmetry.assemblies.length > 0,
apply({ a, params }) {
return Task.create('Build Assembly', async ctx => {
let id = params.id;
const model = a.data.model;
if (!id && model.symmetry.assemblies.length) id = model.symmetry.assemblies[0].id;
const asm = ModelSymmetry.findAssembly(a.data.model, id || '');
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);
})
}
});
export { CreateStructureSelection }
namespace CreateStructureSelection { export interface Params { query: Expression, label?: string } }
const CreateStructureSelection = PluginStateTransform.Create<SO.Structure, SO.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],
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);
}
});
......@@ -8,7 +8,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 { CartoonRepresentation, DefaultCartoonProps } from 'mol-repr/structure/representation/cartoon';
import { BallAndStickRepresentation, DefaultBallAndStickProps } from 'mol-repr/structure/representation/ball-and-stick';
export { CreateStructureRepresentation }
namespace CreateStructureRepresentation { export interface Params { } }
......@@ -18,8 +19,8 @@ const CreateStructureRepresentation = PluginStateTransform.Create<SO.Structure,
to: [SO.StructureRepresentation3D],
apply({ a, params }) {
return Task.create('Structure Representation', async ctx => {
const repr = CartoonRepresentation();
await repr.createOrUpdate({ /* TODO add `webgl: WebGLContext` */ }, { ...DefaultCartoonProps }, a.data).runInContext(ctx);
const repr = BallAndStickRepresentation(); // CartoonRepresentation();
await repr.createOrUpdate({ /* TODO add `webgl: WebGLContext` */ }, DefaultBallAndStickProps, a.data).runInContext(ctx);
return new SO.StructureRepresentation3D({ label: 'Cartoon' }, { repr });
});
},
......
......@@ -7,6 +7,7 @@
import * as React from 'react';
import { PluginContext } from '../context';
import { PluginStateObject } from 'mol-plugin/state/base';
import { StateObject } from 'mol-state'
export class Tree extends React.Component<{ plugin: PluginContext }, { }> {
......@@ -25,8 +26,14 @@ export class TreeNode extends React.Component<{ plugin: PluginContext, nodeRef:
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)!;
if (!obj.obj) {
return <div style={{ borderLeft: '1px solid black', paddingLeft: '5px' }}>
{(obj.obj!.props as PluginStateObject.Props).label}
{StateObject.StateType[obj.state]} {obj.errorText}
</div>;
}
const props = obj.obj!.props as PluginStateObject.Props;
return <div style={{ borderLeft: '1px solid black', paddingLeft: '5px' }}>
{props.label} {props.description ? <small>{props.description}</small> : void 0}
{n.children.size === 0
? void 0
: <div style={{ marginLeft: '10px' }}>{n.children.map(c => <TreeNode plugin={this.props.plugin} nodeRef={c!} key={c} />)}</div>
......
......@@ -54,7 +54,7 @@ class State {
});
}
constructor(rootObject: StateObject, params?: { globalContext?: unknown, defaultObjectProps: unknown }) {
constructor(rootObject: StateObject, params?: { globalContext?: unknown, defaultObjectProps?: unknown }) {
const tree = this._tree;
const root = tree.getValue(tree.rootRef)!;
const defaultObjectProps = (params && params.defaultObjectProps) || { }
......@@ -82,7 +82,7 @@ namespace State {
readonly props: { [key: string]: unknown }
}
export function create(rootObject: StateObject, params?: { globalContext?: unknown, defaultObjectProps: unknown }) {
export function create(rootObject: StateObject, params?: { globalContext?: unknown, defaultObjectProps?: unknown }) {
return new State(rootObject, params);
}
}
......
......@@ -20,7 +20,7 @@ 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 To<T extends Transformer<any, any, any>> = T extends Transformer<any, infer B, any> ? B : unknown;
export type ControlsFor<A extends StateObject, Props> = { [P in keyof Props]?: ((a: A, globalCtx: unknown) => PD.Any) }
export type ControlsFor<A extends StateObject, Props> = { [P in keyof Props]?: PD.Any }
export interface ApplyParams<A extends StateObject = StateObject, P = unknown> {
a: A,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment