/** * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> */ import { Transformer, Transform, State } from 'mol-state'; import { Canvas3D } from 'mol-canvas3d/canvas3d'; import { StateTransforms } from './state/transforms'; import { PluginStateObject as SO } from './state/objects'; import { RxEventHelper } from 'mol-util/rx-event-helper'; import { PluginState } from './state'; import { PluginCommand, PluginCommands } from './command'; import { Task } from 'mol-task'; import { merge } from 'rxjs'; import { PluginBehaviors, BuiltInPluginBehaviors } from './behavior'; import { Loci, EmptyLoci } from 'mol-model/loci'; import { Representation } from 'mol-repr'; import { CreateStructureFromPDBe } from './state/actions/basic'; import { LogEntry } from 'mol-util/log-entry'; import { TaskManager } from './util/task-manager'; export class PluginContext { private disposed = false; private ev = RxEventHelper.create(); private tasks = new TaskManager(); readonly state = new PluginState(this); readonly commands = new PluginCommand.Manager(); readonly events = { state: { data: this.state.data.events, behavior: this.state.behavior.events, cameraSnapshots: this.state.cameraSnapshots.events, snapshots: this.state.snapshots.events, }, log: this.ev<LogEntry>(), task: this.tasks.events }; readonly behaviors = { state: { data: this.state.data.behaviors, behavior: this.state.behavior.behaviors }, canvas: { highlightLoci: this.ev.behavior<{ loci: Loci, repr?: Representation.Any }>({ loci: EmptyLoci }), selectLoci: this.ev.behavior<{ loci: Loci, repr?: Representation.Any }>({ loci: EmptyLoci }), }, command: this.commands.behaviour }; readonly canvas3d: Canvas3D; initViewer(canvas: HTMLCanvasElement, container: HTMLDivElement) { try { (this.canvas3d as Canvas3D) = Canvas3D.create(canvas, container); this.canvas3d.animate(); console.log('canvas3d created'); return true; } catch (e) { console.error(e); return false; } } log(e: LogEntry) { this.events.log.next(e); } /** * This should be used in all transform related request so that it could be "spoofed" to allow * "static" access to resources. */ async fetch(url: string, type: 'string' | 'binary' = 'string'): Promise<string | Uint8Array> { const req = await fetch(url, { referrerPolicy: 'origin-when-cross-origin' }); return type === 'string' ? await req.text() : new Uint8Array(await req.arrayBuffer()); } runTask<T>(task: Task<T>) { return this.tasks.run(task); } dispose() { if (this.disposed) return; this.commands.dispose(); this.canvas3d.dispose(); this.ev.dispose(); this.state.dispose(); this.tasks.dispose(); this.disposed = true; } private initBuiltInBehavior() { BuiltInPluginBehaviors.State.registerDefault(this); BuiltInPluginBehaviors.Representation.registerDefault(this); BuiltInPluginBehaviors.Camera.registerDefault(this); merge(this.state.data.events.log, this.state.behavior.events.log).subscribe(e => this.events.log.next(e)); } async _test_initBehaviors() { const tree = this.state.behavior.tree.build() .toRoot().apply(PluginBehaviors.Representation.HighlightLoci, { ref: PluginBehaviors.Representation.HighlightLoci.id }) .toRoot().apply(PluginBehaviors.Representation.SelectLoci, { ref: PluginBehaviors.Representation.SelectLoci.id }) .getTree(); await this.runTask(this.state.behavior.update(tree)); } _test_initDataActions() { this.state.data.actions .add(CreateStructureFromPDBe) .add(StateTransforms.Data.Download) .add(StateTransforms.Data.ParseCif) .add(StateTransforms.Model.CreateStructureAssembly) .add(StateTransforms.Model.CreateStructure) .add(StateTransforms.Model.CreateModelFromTrajectory) .add(StateTransforms.Visuals.CreateStructureRepresentation); } applyTransform(state: State, a: Transform.Ref, transformer: Transformer, params: any) { const tree = state.tree.build().to(a).apply(transformer, params); return PluginCommands.State.Update.dispatch(this, { state, tree }); } updateTransform(state: State, a: Transform.Ref, params: any) { const tree = state.build().to(a).update(params); return PluginCommands.State.Update.dispatch(this, { state, tree }); } private initEvents() { merge(this.events.state.data.object.created, this.events.state.behavior.object.created).subscribe(o => { 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 (!SO.isBehavior(o.obj)) return; o.obj.data.unregister(); }); merge(this.events.state.data.object.updated, this.events.state.behavior.object.updated).subscribe(o => { if (o.action === 'recreate') { if (o.oldObj && SO.isBehavior(o.oldObj)) o.oldObj.data.unregister(); if (o.obj && SO.isBehavior(o.obj)) o.obj.data.register(); } }); } constructor() { this.initEvents(); this.initBuiltInBehavior(); this._test_initBehaviors(); this._test_initDataActions(); } // logger = ; // settings = ; }