|
@@ -8,7 +8,7 @@ import { StateAction, StateBuilder, StateTransformer, State } from '../../../../
|
|
|
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 { Ingredient, IngredientSource, CellPacking } from './data';
|
|
|
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';
|
|
@@ -34,44 +34,56 @@ function getCellPackModelUrl(fileName: string, baseUrl: string) {
|
|
|
return `${baseUrl}/results/${fileName}`
|
|
|
}
|
|
|
|
|
|
-async function getModel(id: string, baseUrl: string, file?: File) {
|
|
|
+async function getModel(id: string, model_id:number, baseUrl: string, file?: File) {
|
|
|
let model: Model;
|
|
|
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]
|
|
|
+ model = (await trajectoryFromMmCIF(cif).run())[model_id]
|
|
|
} else if (file.name.endsWith('.pdb')) {
|
|
|
const pdb = await parsePDBfile(text, id)
|
|
|
|
|
|
- model = (await trajectoryFromPDB(pdb).run())[0]
|
|
|
+ model = (await trajectoryFromPDB(pdb).run())[model_id]
|
|
|
} 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]
|
|
|
+ model = (await trajectoryFromMmCIF(cif).run())[model_id]
|
|
|
} else {
|
|
|
const pdb = await getFromCellPackDB(id, baseUrl)
|
|
|
- model = (await trajectoryFromPDB(pdb).run())[0]
|
|
|
+ model = (await trajectoryFromPDB(pdb).run())[model_id]
|
|
|
}
|
|
|
return model
|
|
|
}
|
|
|
|
|
|
-async function getStructure(model: Model, props: { assembly?: string } = {}) {
|
|
|
+async function getStructure(model: Model, source:IngredientSource,props: { assembly?: string } = {}) {
|
|
|
let structure = Structure.ofModel(model)
|
|
|
const { assembly } = props
|
|
|
|
|
|
if (assembly) {
|
|
|
structure = await StructureSymmetry.buildAssembly(structure, assembly).run()
|
|
|
}
|
|
|
+ let query;
|
|
|
+ if (source.selection){
|
|
|
+ const asymIds:string[] = source.selection.replace(" :","").split(" or")
|
|
|
+ query = MS.struct.modifier.union([
|
|
|
+ MS.struct.generator.atomGroups({
|
|
|
+ 'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
|
|
|
+ 'chain-test': MS.core.set.has([MS.set(...asymIds), MS.ammp('auth_asym_id')])
|
|
|
+ })
|
|
|
+ ])
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ query = MS.struct.modifier.union([
|
|
|
+ MS.struct.generator.atomGroups({
|
|
|
+ 'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer'])
|
|
|
+ })
|
|
|
+ ])
|
|
|
+ }
|
|
|
|
|
|
- const query = MS.struct.modifier.union([
|
|
|
- MS.struct.generator.atomGroups({
|
|
|
- 'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer'])
|
|
|
- })
|
|
|
- ])
|
|
|
const compiled = compile<StructureSelection>(query)
|
|
|
const result = compiled(new QueryContext(structure))
|
|
|
structure = StructureSelection.unionStructure(result)
|
|
@@ -79,7 +91,7 @@ async function getStructure(model: Model, props: { assembly?: string } = {}) {
|
|
|
return structure
|
|
|
}
|
|
|
|
|
|
-function getTransform(trans: Vec3, rot: Quat) {
|
|
|
+function getTransformLegacy(trans: Vec3, rot: Quat) {
|
|
|
const q: Quat = Quat.create(-rot[3], rot[0], rot[1], rot[2])
|
|
|
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q)
|
|
|
Mat4.transpose(m, m)
|
|
@@ -88,14 +100,24 @@ function getTransform(trans: Vec3, rot: Quat) {
|
|
|
return m
|
|
|
}
|
|
|
|
|
|
-function getResultTransforms(results: Ingredient['results']) {
|
|
|
- return results.map((r: Ingredient['results'][0]) => getTransform(r[0], r[1]))
|
|
|
+function getTransform(trans: Vec3, rot: Quat) {
|
|
|
+ const q: Quat = Quat.create(rot[0], rot[1], rot[2], rot[3])
|
|
|
+ const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q)
|
|
|
+ const p: Vec3 = Vec3.create(trans[0],trans[1],trans[2])
|
|
|
+ Mat4.setTranslation(m, p)
|
|
|
+ return m
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function getResultTransforms(results: Ingredient['results'],legacy:boolean) {
|
|
|
+ if (legacy) return results.map((r: Ingredient['results'][0]) => getTransformLegacy(r[0], r[1]))
|
|
|
+ else return results.map((r: Ingredient['results'][0]) => getTransform(r[0], r[1]))
|
|
|
}
|
|
|
|
|
|
function getCurveTransforms(ingredient: Ingredient) {
|
|
|
const n = ingredient.nbCurve || 0
|
|
|
const instances: Mat4[] = []
|
|
|
-
|
|
|
+ const segmentLength = (ingredient.radii)? ((ingredient.radii[0].radii)?ingredient.radii[0].radii[0]*2.0:3.4):3.4;
|
|
|
for (let i = 0; i < n; ++i) {
|
|
|
const cname = `curve${i}`
|
|
|
if (!(cname in ingredient)) {
|
|
@@ -109,7 +131,7 @@ function getCurveTransforms(ingredient: Ingredient) {
|
|
|
}
|
|
|
const points = new Float32Array(_points.length * 3)
|
|
|
for (let i = 0, il = _points.length; i < il; ++i) Vec3.toArray(_points[i], points, i * 3)
|
|
|
- const newInstances = getMatFromResamplePoints(points)
|
|
|
+ const newInstances = getMatFromResamplePoints(points,segmentLength)
|
|
|
instances.push(...newInstances)
|
|
|
}
|
|
|
|
|
@@ -224,7 +246,7 @@ function getCifCurve(name: string, transforms: Mat4[], model: Model) {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
-async function getCurve(name: string, transforms: Mat4[], model: Model) {
|
|
|
+async function getCurve(name: string, ingredient:Ingredient, transforms: Mat4[], model: Model) {
|
|
|
const cif = getCifCurve(name, transforms, model)
|
|
|
|
|
|
const curveModelTask = Task.create('Curve Model', async ctx => {
|
|
@@ -234,7 +256,7 @@ async function getCurve(name: string, transforms: Mat4[], model: Model) {
|
|
|
})
|
|
|
|
|
|
const curveModel = await curveModelTask.run()
|
|
|
- return getStructure(curveModel)
|
|
|
+ return getStructure(curveModel, ingredient.source)
|
|
|
}
|
|
|
|
|
|
async function getIngredientStructure(ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles) {
|
|
@@ -251,14 +273,55 @@ async function getIngredientStructure(ingredient: Ingredient, baseUrl: string, i
|
|
|
if (name === 'lypoglycane') return
|
|
|
}
|
|
|
|
|
|
- const model = await getModel(source.pdb || name, baseUrl, file)
|
|
|
+ //model id in case structure is NMR
|
|
|
+ const model_id = (ingredient.source.model)? parseInt(ingredient.source.model) : 0;
|
|
|
+ const model = await getModel(source.pdb || name,model_id, baseUrl, file)
|
|
|
if (!model) return
|
|
|
|
|
|
if (nbCurve) {
|
|
|
- return getCurve(name, getCurveTransforms(ingredient), model)
|
|
|
+ return getCurve(name, ingredient, getCurveTransforms(ingredient), model)
|
|
|
} else {
|
|
|
- const structure = await getStructure(model, { assembly: source.biomt ? '1' : undefined })
|
|
|
- return getAssembly(getResultTransforms(results), structure)
|
|
|
+ let bu:string|undefined = source.bu ? source.bu : undefined;
|
|
|
+ if (bu){
|
|
|
+ if (bu === "AU") {bu = undefined;}
|
|
|
+ else {
|
|
|
+ bu = bu.slice(2)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let structure = await getStructure(model,source, { assembly: bu })
|
|
|
+ //transform with offset and pcp
|
|
|
+ let legacy:boolean = true
|
|
|
+ if (ingredient.offset || ingredient.principalAxis)
|
|
|
+ //center the structure
|
|
|
+ {
|
|
|
+ legacy=false
|
|
|
+ const boundary = structure.boundary
|
|
|
+ let structureCenter:Vec3 = Vec3.zero()
|
|
|
+ Vec3.negate(structureCenter,boundary.sphere.center)
|
|
|
+ const m1: Mat4 = Mat4.identity()
|
|
|
+ Mat4.setTranslation(m1, structureCenter)
|
|
|
+ structure = Structure.transform(structure,m1)
|
|
|
+ if (ingredient.offset)
|
|
|
+ {
|
|
|
+ if (!Vec3.exactEquals(ingredient.offset,Vec3.zero()))
|
|
|
+ {
|
|
|
+ const m: Mat4 = Mat4.identity();
|
|
|
+ Mat4.setTranslation(m, ingredient.offset)
|
|
|
+ structure = Structure.transform(structure,m);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (ingredient.principalAxis)
|
|
|
+ {
|
|
|
+ if (!Vec3.exactEquals(ingredient.principalAxis,Vec3.unitZ))
|
|
|
+ {
|
|
|
+ const q: Quat = Quat.identity();
|
|
|
+ Quat.rotationTo(q,ingredient.principalAxis,Vec3.unitZ)
|
|
|
+ const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q)
|
|
|
+ structure = Structure.transform(structure,m);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return getAssembly(getResultTransforms(results,legacy), structure)
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -319,6 +382,46 @@ async function handleHivRna(ctx: { runtime: RuntimeContext, fetch: AjaxTask }, p
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+async function loadMembrane(name:string, plugin: PluginContext, runtime: RuntimeContext, state: State, params: LoadCellPackModelParams) {
|
|
|
+ const fname: string = `${name}.bcif`
|
|
|
+ let ingredientFiles: IngredientFiles = {}
|
|
|
+ if (params.ingredients.files !== null) {
|
|
|
+ for (let i = 0, il = params.ingredients.files.length; i < il; ++i) {
|
|
|
+ const file = params.ingredients.files.item(i)
|
|
|
+ if (file) ingredientFiles[file.name] = file
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const url = `${params.baseUrl}/membranes/${name}.bcif`
|
|
|
+ //
|
|
|
+ const file = (ingredientFiles)?ingredientFiles[fname]:null;
|
|
|
+ let membrane
|
|
|
+ if (!file)
|
|
|
+ //can we check if url exist
|
|
|
+ {
|
|
|
+ membrane = await state.build().toRoot()
|
|
|
+ .apply(StateTransforms.Data.Download, { label: name, url, isBinary: true }, { state: { isGhost: true } })
|
|
|
+ .apply(StateTransforms.Data.ParseCif, undefined, { state: { isGhost: true } })
|
|
|
+ .apply(StateTransforms.Model.TrajectoryFromMmCif, undefined, { state: { isGhost: true } })
|
|
|
+ .apply(StateTransforms.Model.ModelFromTrajectory, undefined, { state: { isGhost: true } })
|
|
|
+ .apply(StateTransforms.Model.StructureFromModel)
|
|
|
+ .commit()
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ membrane = await state.build().toRoot()
|
|
|
+ .apply(StateTransforms.Data.ReadFile, { file, isBinary: true, label: file.name }, { state: { isGhost: true } })
|
|
|
+ .apply(StateTransforms.Data.ParseCif, undefined, { state: { isGhost: true } })
|
|
|
+ .apply(StateTransforms.Model.TrajectoryFromMmCif, undefined, { state: { isGhost: true } })
|
|
|
+ .apply(StateTransforms.Model.ModelFromTrajectory, undefined, { state: { isGhost: true } })
|
|
|
+ .apply(StateTransforms.Model.StructureFromModel)
|
|
|
+ .commit()
|
|
|
+ }
|
|
|
+
|
|
|
+ const membraneParams = {
|
|
|
+ representation: params.preset.representation,
|
|
|
+ }
|
|
|
+ await CellpackMembranePreset.apply(membrane, membraneParams, plugin)
|
|
|
+}
|
|
|
+
|
|
|
async function loadHivMembrane(plugin: PluginContext, runtime: RuntimeContext, state: State, params: LoadCellPackModelParams) {
|
|
|
const url = `${params.baseUrl}/membranes/hiv_lipids.bcif`
|
|
|
const membrane = await state.build().toRoot()
|
|
@@ -381,6 +484,9 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
|
|
|
representation: params.preset.representation,
|
|
|
}
|
|
|
await CellpackPackingPreset.apply(packing, packingParams, plugin)
|
|
|
+ if ( packings[i].location === "surface" ){
|
|
|
+ await loadMembrane(packings[i].name, plugin, runtime, state, params)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -392,6 +498,7 @@ const LoadCellPackModelParams = {
|
|
|
['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV-1_0.1.6-8_mixed_radii_pdb'],
|
|
|
['hiv_lipids.bcif', 'hiv_lipids'],
|
|
|
['influenza_model1.json', 'influenza_model1'],
|
|
|
+ ['ExosomeModel.json', 'ExosomeModel'],
|
|
|
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma1.5_mixed_pdb_fixed'],
|
|
|
] as const),
|
|
|
'file': PD.File({ accept: 'id' }),
|