diff --git a/src/mol-plugin/index.ts b/src/mol-plugin/index.ts index a0290122233d5b104399c40987aac1bbb4e1054e..7b77eaebe11e4caad1030c7e27cebf0439b138ac 100644 --- a/src/mol-plugin/index.ts +++ b/src/mol-plugin/index.ts @@ -22,6 +22,7 @@ import { VolumeStreamingCustomControls } from '../mol-plugin-ui/custom/volume'; export const DefaultPluginSpec: PluginSpec = { actions: [ PluginSpec.Action(StateActions.Structure.DownloadStructure), + PluginSpec.Action(StateActions.Structure.AddTrajectoryFromModelAndCoordinates), PluginSpec.Action(StateActions.Volume.DownloadDensity), PluginSpec.Action(StateActions.DataFormat.OpenFile), PluginSpec.Action(StateActions.Structure.Create3DRepresentationPreset), diff --git a/src/mol-plugin/state/actions/data-format.ts b/src/mol-plugin/state/actions/data-format.ts index f2d99a56c1b1e0c40880c5c6fe8989fe59794bba..4159b65f017adcfdae629c3f88a75d5456a65477 100644 --- a/src/mol-plugin/state/actions/data-format.ts +++ b/src/mol-plugin/state/actions/data-format.ts @@ -12,7 +12,7 @@ import { PluginStateObject } from '../objects'; import { ParamDefinition as PD } from '../../../mol-util/param-definition'; import { Ccp4Provider, Dsn6Provider, DscifProvider } from './volume'; import { StateTransforms } from '../transforms'; -import { MmcifProvider, PdbProvider, GroProvider, Provider3dg } from './structure'; +import { MmcifProvider, PdbProvider, GroProvider, Provider3dg, DcdProvider } from './structure'; import msgpackDecode from '../../../mol-io/common/msgpack/decode' import { PlyProvider } from './shape'; @@ -57,6 +57,7 @@ export class DataFormatRegistry<D extends PluginStateObject.Data.Binary | Plugin constructor() { this.add('3dg', Provider3dg) this.add('ccp4', Ccp4Provider) + this.add('dcd', DcdProvider) this.add('dscif', DscifProvider) this.add('dsn6', Dsn6Provider) this.add('gro', GroProvider) @@ -155,7 +156,7 @@ type cifVariants = 'dscif' | -1 export function guessCifVariant(info: FileInfo, data: Uint8Array | string): cifVariants { if (info.ext === 'bcif') { try { - // TODO find a way to run msgpackDecode only once + // TODO: find a way to run msgpackDecode only once // now it is run twice, here and during file parsing if (msgpackDecode(data as Uint8Array).encoder.startsWith('VolumeServer')) return 'dscif' } catch { } diff --git a/src/mol-plugin/state/actions/structure.ts b/src/mol-plugin/state/actions/structure.ts index aed5ca368bed73ee9f3565846fb36ef4b844c20a..f75e6894467cb6a198185014348b6a254e43d6e4 100644 --- a/src/mol-plugin/state/actions/structure.ts +++ b/src/mol-plugin/state/actions/structure.ts @@ -11,7 +11,7 @@ import { ParamDefinition as PD } from '../../../mol-util/param-definition'; import { PluginStateObject } from '../objects'; import { StateTransforms } from '../transforms'; import { Download } from '../transforms/data'; -import { CustomModelProperties, StructureSelectionFromExpression, CustomStructureProperties } from '../transforms/model'; +import { CustomModelProperties, StructureSelectionFromExpression, CustomStructureProperties, CoordinatesFromDcd, TrajectoryFromModelAndCoordinates } from '../transforms/model'; import { DataFormatProvider, guessCifVariant, DataFormatBuilderOptions } from './data-format'; import { FileInfo } from '../../../mol-util/file-info'; import { Task } from '../../../mol-task'; @@ -86,6 +86,21 @@ export const Provider3dg: DataFormatProvider<any> = { } } +export const DcdProvider: DataFormatProvider<any> = { + label: 'DCD', + description: 'DCD', + stringExtensions: [], + binaryExtensions: ['dcd'], + isApplicable: (info: FileInfo, data: string) => { + return info.ext === 'dcd' + }, + getDefaultBuilder: (ctx: PluginContext, data: StateBuilder.To<PluginStateObject.Data.Binary>, options: DataFormatBuilderOptions, state: State) => { + return Task.create('DCD default builder', async taskCtx => { + await state.updateTree(data.apply(CoordinatesFromDcd)).runInContext(taskCtx) + }) + } +} + type StructureFormat = 'pdb' | 'cif' | 'gro' | '3dg' // @@ -395,4 +410,29 @@ export const StructureFromSelection = StateAction.build({ const expression = StructureElement.Loci.toExpression(sel); const root = state.build().to(ref).apply(StructureSelectionFromExpression, { expression, label: params.label }); return state.updateTree(root); +}); + +export const AddTrajectoryFromModelAndCoordinates = StateAction.build({ + display: { name: 'Add Trajectory', description: 'Add trajectory from existing model and coordinates.' }, + from: PluginStateObject.Root, + params(a, ctx: PluginContext) { + const state = ctx.state.dataState + const models = state.selectQ(q => q.rootsOfType(PluginStateObject.Molecule.Model)) + const modelOptions = models.map(m => [m.transform.ref, m.obj!.label]) as [string, string][] + const coords = state.selectQ(q => q.rootsOfType(PluginStateObject.Molecule.Coordinates)) + const coordsOptions = coords.map(c => [c.transform.ref, c.obj!.label]) as [string, string][] + return { + model: PD.Select(modelOptions.length ? modelOptions[0][0] : '', modelOptions), + coordinates: PD.Select(coordsOptions.length ? coordsOptions[0][0] : '', coordsOptions) + } + } +})(({ ref, params, state }, ctx: PluginContext) => { + const dependsOn = [params.model, params.coordinates]; + const root = state.build().toRoot() + .apply(TrajectoryFromModelAndCoordinates, { + modelRef: params.model, + coordinatesRef: params.coordinates + }, { dependsOn }) + .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 }) + return state.updateTree(createStructureAndVisuals(ctx, root, false)); }); \ No newline at end of file diff --git a/src/mol-plugin/state/objects.ts b/src/mol-plugin/state/objects.ts index 499c10b41335a879108ed9da2aa46c9aef3f1661..4f59657aa4bffab84e6c3d8506f350bc3140abb6 100644 --- a/src/mol-plugin/state/objects.ts +++ b/src/mol-plugin/state/objects.ts @@ -7,6 +7,7 @@ import { CifFile } from '../../mol-io/reader/cif'; import { PlyFile } from '../../mol-io/reader/ply/schema'; +import { Coordinates as _Coordinates } from '../../mol-model/structure'; import { Model as _Model, Structure as _Structure, StructureElement } from '../../mol-model/structure'; import { VolumeData } from '../../mol-model/volume'; import { PluginBehavior } from '../../mol-plugin/behavior/behavior'; @@ -19,6 +20,8 @@ import { Dsn6File } from '../../mol-io/reader/dsn6/schema'; import { ShapeRepresentation } from '../../mol-repr/shape/representation'; import { Shape as _Shape } from '../../mol-model/shape'; import { ShapeProvider } from '../../mol-model/shape/provider'; +import { File3DG } from '../../mol-io/reader/3dg/parser'; +import { DcdFile } from '../../mol-io/reader/dcd/parser'; export type TypeClass = 'root' | 'data' | 'prop' @@ -64,6 +67,10 @@ export namespace PluginStateObject { export namespace Format { export class Json extends Create<any>({ name: 'JSON Data', typeClass: 'Data' }) { } export class Cif extends Create<CifFile>({ name: 'CIF File', typeClass: 'Data' }) { } + export class Pdb extends Create<CifFile>({ name: 'PDB File', typeClass: 'Data' }) { } + export class Gro extends Create<CifFile>({ name: 'GRO File', typeClass: 'Data' }) { } + export class _3dg extends Create<CifFile>({ name: '3DG File', typeClass: 'Data' }) { } + export class Dcd extends Create<CifFile>({ name: 'DCD File', typeClass: 'Data' }) { } export class Ply extends Create<PlyFile>({ name: 'PLY File', typeClass: 'Data' }) { } export class Ccp4 extends Create<Ccp4File>({ name: 'CCP4/MRC/MAP File', typeClass: 'Data' }) { } export class Dsn6 extends Create<Dsn6File>({ name: 'DSN6/BRIX File', typeClass: 'Data' }) { } @@ -73,6 +80,10 @@ export namespace PluginStateObject { | { kind: 'string', data: string } | { kind: 'binary', data: Uint8Array } | { kind: 'cif', data: CifFile } + | { kind: 'pdb', data: CifFile } + | { kind: 'gro', data: CifFile } + | { kind: '3dg', data: File3DG } + | { kind: 'dcd', data: DcdFile } | { kind: 'ccp4', data: Ccp4File } | { kind: 'dsn6', data: Dsn6File } | { kind: 'ply', data: PlyFile } @@ -83,8 +94,9 @@ export namespace PluginStateObject { } export namespace Molecule { - export class Trajectory extends Create<ReadonlyArray<_Model>>({ name: 'Trajectory', typeClass: 'Object' }) { } + export class Coordinates extends Create<_Coordinates>({ name: 'Coordinates', typeClass: 'Object' }) { } export class Model extends Create<_Model>({ name: 'Model', typeClass: 'Object' }) { } + export class Trajectory extends Create<ReadonlyArray<_Model>>({ name: 'Trajectory', typeClass: 'Object' }) { } export class Structure extends Create<_Structure>({ name: 'Structure', typeClass: 'Object' }) { } export namespace Structure { diff --git a/src/mol-plugin/state/transforms/model.ts b/src/mol-plugin/state/transforms/model.ts index 4f7610472b5a421d7aa1447320ac84cf243babfd..cd86bd9bcb8a9d5daf41cc5742e3861d6278fb3b 100644 --- a/src/mol-plugin/state/transforms/model.ts +++ b/src/mol-plugin/state/transforms/model.ts @@ -9,7 +9,7 @@ import { parsePDB } from '../../../mol-io/reader/pdb/parser'; import { Vec3, Mat4, Quat } from '../../../mol-math/linear-algebra'; import { trajectoryFromMmCIF } from '../../../mol-model-formats/structure/mmcif'; import { trajectoryFromPDB } from '../../../mol-model-formats/structure/pdb'; -import { Model, Queries, QueryContext, Structure, StructureQuery, StructureSelection as Sel, StructureElement } from '../../../mol-model/structure'; +import { Model, Queries, QueryContext, Structure, StructureQuery, StructureSelection as Sel, StructureElement, trajectoryFromModelAndCoordinates, Coordinates } from '../../../mol-model/structure'; import { PluginContext } from '../../../mol-plugin/context'; import { MolScriptBuilder } from '../../../mol-script/language/builder'; import Expression from '../../../mol-script/language/expression'; @@ -28,7 +28,11 @@ import { trajectoryFrom3DG } from '../../../mol-model-formats/structure/3dg'; import { StructureSelectionQueries } from '../../util/structure-selection-helper'; import { StructureQueryHelper } from '../../util/structure-query'; import { ModelStructureRepresentation } from '../representation/model'; +import { parseDcd } from '../../../mol-io/reader/dcd/parser'; +import { coordinatesFromDcd } from '../../../mol-model-formats/structure/dcd'; +export { CoordinatesFromDcd }; +export { TrajectoryFromModelAndCoordinates }; export { TrajectoryFromBlob }; export { TrajectoryFromMmCif }; export { TrajectoryFromPDB }; @@ -48,6 +52,45 @@ export { StructureComplexElement }; export { CustomModelProperties }; export { CustomStructureProperties }; +type CoordinatesFromDcd = typeof CoordinatesFromDcd +const CoordinatesFromDcd = PluginStateTransform.BuiltIn({ + name: 'coordinates-from-dcd', + display: { name: 'Parse DCD', description: 'Parse DCD binary data.' }, + from: [SO.Data.Binary], + to: SO.Molecule.Coordinates +})({ + apply({ a }) { + return Task.create('Parse DCD', async ctx => { + const parsed = await parseDcd(a.data).runInContext(ctx); + if (parsed.isError) throw new Error(parsed.message); + const coordinates = await coordinatesFromDcd(parsed.result).runInContext(ctx); + return new SO.Molecule.Coordinates(coordinates, { label: a.label, description: a.description }); + }); + } +}); + +type TrajectoryFromModelAndCoordinates = typeof TrajectoryFromModelAndCoordinates +const TrajectoryFromModelAndCoordinates = PluginStateTransform.BuiltIn({ + name: 'trajectory-from-model-and-coordinates', + display: { name: 'Trajectory from Model & Coordinates', description: 'Create a trajectory from existing model and coordinates.' }, + from: SO.Root, + to: SO.Molecule.Trajectory, + params: { + modelRef: PD.Text('', { isHidden: true }), + coordinatesRef: PD.Text('', { isHidden: true }), + } +})({ + apply({ params, dependencies }) { + return Task.create('Create trajectory from model and coordinates', async ctx => { + const model = dependencies![params.modelRef].data as Model + const coordinates = dependencies![params.coordinatesRef].data as Coordinates + const trajectory = trajectoryFromModelAndCoordinates(model, coordinates); + const props = { label: 'Trajectory', description: `${trajectory.length} model${trajectory.length === 1 ? '' : 's'}` }; + return new SO.Molecule.Trajectory(trajectory, props); + }); + } +}); + type TrajectoryFromBlob = typeof TrajectoryFromBlob const TrajectoryFromBlob = PluginStateTransform.BuiltIn({ name: 'trajectory-from-blob', @@ -66,7 +109,7 @@ const TrajectoryFromBlob = PluginStateTransform.BuiltIn({ for (const x of xs) models.push(x); } - const props = { label: `Trajectory`, description: `${models.length} model${models.length === 1 ? '' : 's'}` }; + const props = { label: 'Trajectory', description: `${models.length} model${models.length === 1 ? '' : 's'}` }; return new SO.Molecule.Trajectory(models, props); }); } @@ -239,7 +282,7 @@ const StructureAssemblyFromModel = PluginStateTransform.BuiltIn({ } }); -const _translation = Vec3.zero(), _m = Mat4.zero(), _n = Mat4.zero(); +const _translation = Vec3(), _m = Mat4(), _n = Mat4(); type TransformStructureConformation = typeof TransformStructureConformation const TransformStructureConformation = PluginStateTransform.BuiltIn({ name: 'transform-structure-conformation', @@ -267,14 +310,14 @@ const TransformStructureConformation = PluginStateTransform.BuiltIn({ Mat4.mul3(m, _n, rot, _m); const s = Structure.transform(a.data, m); - const props = { label: `${a.label}`, description: `Transformed` }; + const props = { label: `${a.label}`, description: 'Transformed' }; return new SO.Molecule.Structure(s, props); }, interpolate(src, tar, t) { // TODO: optimize - const u = Mat4.fromRotation(Mat4.zero(), Math.PI / 180 * src.angle, Vec3.normalize(Vec3.zero(), src.axis)); + const u = Mat4.fromRotation(Mat4.zero(), Math.PI / 180 * src.angle, Vec3.normalize(Vec3(), src.axis)); Mat4.setTranslation(u, src.translation); - const v = Mat4.fromRotation(Mat4.zero(), Math.PI / 180 * tar.angle, Vec3.normalize(Vec3.zero(), tar.axis)); + const v = Mat4.fromRotation(Mat4.zero(), Math.PI / 180 * tar.angle, Vec3.normalize(Vec3(), tar.axis)); Mat4.setTranslation(v, tar.translation); const m = SymmetryOperator.slerp(Mat4.zero(), u, v, t); const rot = Mat4.getRotation(Quat.zero(), m); @@ -300,7 +343,7 @@ const TransformStructureConformationByMatrix = PluginStateTransform.BuiltIn({ }, apply({ a, params }) { const s = Structure.transform(a.data, params.matrix); - const props = { label: `${a.label}`, description: `Transformed` }; + const props = { label: `${a.label}`, description: 'Transformed' }; return new SO.Molecule.Structure(s, props); } });