diff --git a/src/apps/viewer/extensions/cellpack/model.ts b/src/apps/viewer/extensions/cellpack/model.ts index 03b1ce084270e9e2eca585fcbd4c2e91d6fbf957..0c985c078f74bf9d493b8182ac30839aca3ce79e 100644 --- a/src/apps/viewer/extensions/cellpack/model.ts +++ b/src/apps/viewer/extensions/cellpack/model.ts @@ -9,7 +9,7 @@ import { PluginContext } from '../../../../mol-plugin/context'; import { PluginStateObject as PSO } from '../../../../mol-plugin-state/objects'; import { ParamDefinition as PD } from '../../../../mol-util/param-definition'; import { Ingredient, CellPacking } from './data'; -import { getFromPdb, getFromCellPackDB } from './util'; +import { getFromPdb, getFromCellPackDB, IngredientFiles, parseCif, parsePDBfile } from './util'; import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit } from '../../../../mol-model/structure'; import { trajectoryFromMmCIF, MmcifFormat } from '../../../../mol-model-formats/structure/mmcif'; import { trajectoryFromPDB } from '../../../../mol-model-formats/structure/pdb'; @@ -34,9 +34,21 @@ function getCellPackModelUrl(fileName: string, baseUrl: string) { return `${baseUrl}/results/${fileName}` } -async function getModel(id: string, baseUrl: string) { +async function getModel(id: string, baseUrl: string, file?: File) { let model: Model; - if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) { + if (file) { + const text = await file.text() + if (file.name.endsWith('.cif')) { + const cif = (await parseCif(text)).blocks[0] + model = (await trajectoryFromMmCIF(cif).run())[0] + } else if (file.name.endsWith('.pdb')) { + const pdb = await parsePDBfile(text, id) + + model = (await trajectoryFromPDB(pdb).run())[0] + } else { + throw new Error(`unsupported file type '${file.name}'`) + } + } else if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) { // return const cif = await getFromPdb(id) model = (await trajectoryFromMmCIF(cif).run())[0] @@ -225,19 +237,21 @@ async function getCurve(name: string, transforms: Mat4[], model: Model) { return getStructure(curveModel) } -async function getIngredientStructure(ingredient: Ingredient, baseUrl: string) { +async function getIngredientStructure(ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles) { const { name, source, results, nbCurve } = ingredient - - // TODO can these be added to the library? - if (name === 'HIV1_CAhex_0_1_0') return - if (name === 'HIV1_CAhexCyclophilA_0_1_0') return - if (name === 'iLDL') return - if (name === 'peptides') return - if (name === 'lypoglycane') return - if (source.pdb === 'None') return - const model = await getModel(source.pdb || name, baseUrl) + const file = ingredientFiles[source.pdb] + if (!file) { + // TODO can these be added to the library? + if (name === 'HIV1_CAhex_0_1_0') return + if (name === 'HIV1_CAhexCyclophilA_0_1_0') return + if (name === 'iLDL') return + if (name === 'peptides') return + if (name === 'lypoglycane') return + } + + const model = await getModel(source.pdb || name, baseUrl, file) if (!model) return if (nbCurve) { @@ -248,13 +262,13 @@ async function getIngredientStructure(ingredient: Ingredient, baseUrl: string) { } } -export function createStructureFromCellPack(packing: CellPacking, baseUrl: string) { +export function createStructureFromCellPack(packing: CellPacking, baseUrl: string, ingredientFiles: IngredientFiles) { return Task.create('Create Packing Structure', async ctx => { const { ingredients, name } = packing const structures: Structure[] = [] for (const iName in ingredients) { if (ctx.shouldUpdate) await ctx.update(iName) - const s = await getIngredientStructure(ingredients[iName], baseUrl) + const s = await getIngredientStructure(ingredients[iName], baseUrl, ingredientFiles) if (s) structures.push(s) } @@ -345,7 +359,7 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat await handleHivRna({ runtime, fetch: plugin.fetch }, packings, params.baseUrl) for (let i = 0, il = packings.length; i < il; ++i) { - const p = { packing: i, baseUrl: params.baseUrl } + const p = { packing: i, baseUrl: params.baseUrl, ingredientFiles: params.ingredients.files } const packing = state.build().to(cellPackBuilder.ref).apply(StructureFromCellpack, p) await plugin.updateDataState(packing, { revertOnError: true }); @@ -378,6 +392,9 @@ const LoadCellPackModelParams = { 'file': PD.File({ accept: 'id' }), }, { options: [['id', 'Id'], ['file', 'File']] }), baseUrl: PD.Text(DefaultCellPackBaseUrl), + ingredients : PD.Group({ + files: PD.FileList({ accept: '.cif,.pdb' }) + }, { isExpanded: true }), preset: PD.Group({ traceOnly: PD.Boolean(false), representation: PD.Select('gaussian-surface', PD.arrayToOptions(['spacefill', 'gaussian-surface', 'point', 'ellipsoid'])) diff --git a/src/apps/viewer/extensions/cellpack/state.ts b/src/apps/viewer/extensions/cellpack/state.ts index 1e68b486ec540a8add7b71f84e022f5babe8cbfe..abd983c26808377cda7878eaa28a4c12a889c802 100644 --- a/src/apps/viewer/extensions/cellpack/state.ts +++ b/src/apps/viewer/extensions/cellpack/state.ts @@ -9,6 +9,7 @@ import { ParamDefinition as PD } from '../../../../mol-util/param-definition'; import { Task } from '../../../../mol-task'; import { CellPack as _CellPack, Cell, CellPacking } from './data'; import { createStructureFromCellPack } from './model'; +import { IngredientFiles } from './util'; export const DefaultCellPackBaseUrl = 'https://mesoscope.scripps.edu/data/cellPACK_data/cellPACK_database_1.1.0/' @@ -53,20 +54,27 @@ const StructureFromCellpack = PluginStateTransform.BuiltIn({ if (!a) { return { packing: PD.Numeric(0, {}, { description: 'Packing Index' }), - baseUrl: PD.Text(DefaultCellPackBaseUrl) + baseUrl: PD.Text(DefaultCellPackBaseUrl), + ingredientFiles: PD.FileList({ accept: '.cif,.pdb' }) }; } const options = a.data.packings.map((d, i) => [i, d.name] as [number, string]) return { packing: PD.Select(0, options), - baseUrl: PD.Text(DefaultCellPackBaseUrl) + baseUrl: PD.Text(DefaultCellPackBaseUrl), + ingredientFiles: PD.FileList({ accept: '.cif,.pdb' }) } } })({ apply({ a, params }) { return Task.create('Structure from CellPack', async ctx => { const packing = a.data.packings[params.packing] - const structure = await createStructureFromCellPack(packing, params.baseUrl).runInContext(ctx) + const ingredientFiles: IngredientFiles = {} + for (let i = 0, il = params.ingredientFiles.length; i < il; ++i) { + const file = params.ingredientFiles.item(i) + if (file) ingredientFiles[file.name] = file + } + const structure = await createStructureFromCellPack(packing, params.baseUrl, ingredientFiles).runInContext(ctx) return new PSO.Molecule.Structure(structure, { label: packing.name }) }); } diff --git a/src/apps/viewer/extensions/cellpack/util.ts b/src/apps/viewer/extensions/cellpack/util.ts index 6e64ed3dff740caea81d450a2d80286e800470a8..4c36a142b6e00ee79cd384e447660a599ab1da61 100644 --- a/src/apps/viewer/extensions/cellpack/util.ts +++ b/src/apps/viewer/extensions/cellpack/util.ts @@ -7,14 +7,14 @@ import { CIF } from '../../../../mol-io/reader/cif' import { parsePDB } from '../../../../mol-io/reader/pdb/parser'; -async function parseCif(data: string|Uint8Array) { +export async function parseCif(data: string|Uint8Array) { const comp = CIF.parse(data); const parsed = await comp.run(); if (parsed.isError) throw parsed; return parsed.result; } -async function parsePDBfile(data: string, id: string) { +export async function parsePDBfile(data: string, id: string) { const comp = parsePDB(data, id); const parsed = await comp.run(); if (parsed.isError) throw parsed; @@ -45,4 +45,6 @@ export async function getFromCellPackDB(id: string, baseUrl: string) { const name = id.endsWith('.pdb') ? id.substring(0, id.length - 4) : id const parsed = await downloadPDB(getCellPackDataUrl(id, baseUrl), name); return parsed; -} \ No newline at end of file +} + +export type IngredientFiles = { [name: string]: File } \ No newline at end of file