-
David Sehnal authoredDavid Sehnal authored
model.ts 6.47 KiB
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { PluginStateTransform } from '../objects';
import { PluginStateObject as SO } from '../objects';
import { Task } from 'mol-task';
import { Model, Format, Structure, ModelSymmetry, StructureSymmetry, QueryContext, StructureSelection as Sel } 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';
import { MolScriptBuilder } from 'mol-script/language/builder';
export { TrajectoryFromMmCif }
namespace TrajectoryFromMmCif { export interface Params { blockHeader?: string } }
const TrajectoryFromMmCif = PluginStateTransform.Create<SO.Data.Cif, SO.Molecule.Trajectory, TrajectoryFromMmCif.Params>({
name: 'trajectory-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.Molecule.Trajectory],
params(a) {
const { blocks } = a.data;
if (blocks.length === 0) return { };
return {
blockHeader: PD.Select(blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]), { description: 'Header of the block to parse' })
};
},
isApplicable: a => a.data.blocks.length > 0,
apply({ a, params }) {
return Task.create('Parse mmCIF', async ctx => {
const header = params.blockHeader || a.data.blocks[0].header;
const block = a.data.blocks.find(b => b.header === header);
if (!block) throw new Error(`Data block '${[header]}' not found.`);
const models = await Model.create(Format.mmCIF(block)).runInContext(ctx);
if (models.length === 0) throw new Error('No models found.');
const label = { label: models[0].label, description: `${models.length} model${models.length === 1 ? '' : 's'}` };
return new SO.Molecule.Trajectory(models, label);
});
}
});
export { ModelFromTrajectory }
const plus1 = (v: number) => v + 1, minus1 = (v: number) => v - 1;
namespace ModelFromTrajectory { export interface Params { modelIndex: number } }
const ModelFromTrajectory = PluginStateTransform.Create<SO.Molecule.Trajectory, SO.Molecule.Model, ModelFromTrajectory.Params>({
name: 'model-from-trajectory',
display: {
name: 'Model from Trajectory',
description: 'Create a molecular structure from the specified model.'
},
from: [SO.Molecule.Trajectory],
to: [SO.Molecule.Model],
params: a => ({ modelIndex: PD.Converted(plus1, minus1, PD.Numeric(1, { min: 1, max: a.data.length, step: 1 }, { description: 'Model Index' })) }),
isApplicable: a => a.data.length > 0,
apply({ a, params }) {
if (params.modelIndex < 0 || params.modelIndex >= a.data.length) throw new Error(`Invalid modelIndex ${params.modelIndex}`);
const model = a.data[params.modelIndex];
const label = { label: `Model ${model.modelNum}` };
return new SO.Molecule.Model(model, label);
}
});
export { StructureFromModel }
namespace StructureFromModel { export interface Params { } }
const StructureFromModel = PluginStateTransform.Create<SO.Molecule.Model, SO.Molecule.Structure, StructureFromModel.Params>({
name: 'structure-from-model',
display: {
name: 'Structure from Model',
description: 'Create a molecular structure from the specified model.'
},
from: [SO.Molecule.Model],
to: [SO.Molecule.Structure],
apply({ a }) {
let s = Structure.ofModel(a.data);
const label = { label: a.data.label, description: s.elementCount === 1 ? '1 element' : `${s.elementCount} elements` };
return new SO.Molecule.Structure(s, label);
}
});
function structureDesc(s: Structure) {
return s.elementCount === 1 ? '1 element' : `${s.elementCount} elements`;
}
export { StructureAssemblyFromModel }
namespace StructureAssemblyFromModel { export interface Params { /** if not specified, use the 1st */ id?: string } }
const StructureAssemblyFromModel = PluginStateTransform.Create<SO.Molecule.Model, SO.Molecule.Structure, StructureAssemblyFromModel.Params>({
name: 'structure-assembly-from-model',
display: {
name: 'Structure Assembly',
description: 'Create a molecular structure assembly.'
},
from: [SO.Molecule.Model],
to: [SO.Molecule.Structure],
params(a) {
const model = a.data;
const ids = model.symmetry.assemblies.map(a => [a.id, a.id] as [string, string]);
return { id: PD.Select(ids.length ? ids[0][0] : '', ids, { label: 'Asm Id', description: 'Assembly Id' }) };
},
apply({ a, params }) {
return Task.create('Build Assembly', async ctx => {
let id = params.id;
const model = a.data;
if (!id && model.symmetry.assemblies.length) id = model.symmetry.assemblies[0].id;
const asm = ModelSymmetry.findAssembly(model, id || '');
if (!asm) throw new Error(`Assembly '${id}' not found`);
const base = Structure.ofModel(model);
const s = await StructureSymmetry.buildAssembly(base, id!).runInContext(ctx);
const label = { label: `Assembly ${id}`, description: structureDesc(s) };
return new SO.Molecule.Structure(s, label);
})
}
});
export { StructureSelection }
namespace StructureSelection { export interface Params { query: Expression, label?: string } }
const StructureSelection = PluginStateTransform.Create<SO.Molecule.Structure, SO.Molecule.Structure, StructureSelection.Params>({
name: 'structure-selection',
display: {
name: 'Structure Selection',
description: 'Create a molecular structure from the specified model.'
},
from: [SO.Molecule.Structure],
to: [SO.Molecule.Structure],
params: () => ({
query: PD.Value<Expression>(MolScriptBuilder.struct.generator.all, { isHidden: true }),
label: PD.Text('', { isOptional: true, isHidden: true })
}),
apply({ a, params }) {
// TODO: use cache, add "update"
const compiled = compile<Sel>(params.query);
const result = compiled(new QueryContext(a.data));
const s = Sel.unionStructure(result);
const label = { label: `${params.label || 'Selection'}`, description: structureDesc(s) };
return new SO.Molecule.Structure(s, label);
}
});