浏览代码

support for latest cellpack model.
Example is ExosomeModel.json present in the repo

DESKTOP-O6LIMN9\ludov 5 年之前
父节点
当前提交
7266aab4be

+ 3 - 3
src/apps/viewer/extensions/cellpack/curve.ts

@@ -191,9 +191,9 @@ function GetMiniFrame(points: Vec3[], normals: Vec3[]) {
 
 const rpTmpVec1 = Vec3()
 
-export function getMatFromResamplePoints(points: NumberArray) {
-    const segmentLength = 3.4
-    const new_points = ResampleControlPoints(points, 3.4)
+export function getMatFromResamplePoints(points: NumberArray, segmentLength: number) {
+    //const segmentLength = 3.4
+    const new_points = ResampleControlPoints(points, segmentLength)
     const npoints = new_points.length
     const new_normal = GetSmoothNormals(new_points)
     const frames = GetMiniFrame(new_points, new_normal)

+ 29 - 14
src/apps/viewer/extensions/cellpack/data.ts

@@ -27,8 +27,7 @@ export interface Cell {
 
 export interface Recipe {
     setupfile: string
-    /** First entry is name, secound is path: [name: string, path: string][] */
-    paths: [string, string][]
+    paths: [string, string][] // [name: string, path: string][]
     version: string
     name: string
 }
@@ -42,21 +41,37 @@ export interface Packing {
     ingredients: { [key: string]: Ingredient }
 }
 
-export interface Ingredient {
-    source: IngredientSource
-    results: [Vec3, Quat][]
-    name: string
-    positions?: [Vec3[]] // why wrapped in an extra array?
-    radii?: [number[]] // why wrapped in an extra array?
+export interface Positions
+{
+    coords?: Vec3[];
+}
+export interface Radii
+{
+    radii?: number[];
+}
 
+export interface Ingredient {
+    source: IngredientSource;
+    results: [Vec3, Quat][];
+    name: string;
+    positions?: [Positions];//Vec3[]];
+    radii?: [Radii];//number[]];
     /** Number of `curveX` properties in the object where `X` is a 0-indexed number */
-    nbCurve?: number
+    nbCurve?: number;
     /** Curve properties are Vec3[] but that is not expressable in TypeScript */
-    [curveX: string]: unknown
+    [curveX: string]: unknown;
+    principalAxis?: Vec3; /** the orientation in the membrane */
+    offset?: Vec3;  /**offset along membrane */
 }
 
 export interface IngredientSource {
-    pdb: string
-    transform: { center: boolean, translate?: Vec3 }
-    biomt?: boolean
-}
+    pdb: string;
+    bu?:string;  /** biological unit e.g AU,BU1,etc.. */
+    selection?:string; /** NGL selection or :A or :B etc.. */
+    model?:string;     /** model number e.g 0,1,2... */
+    transform: {
+        center: boolean;
+        translate?: Vec3;
+    };
+    biomt?: boolean;
+}

+ 91 - 26
src/apps/viewer/extensions/cellpack/model.ts

@@ -8,8 +8,9 @@ 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 } from './util';
+import { computeStructureBoundary } from '../../../../mol-model/structure/structure/util/boundary';
 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,40 +35,52 @@ function getCellPackModelUrl(fileName: string, baseUrl: string) {
     return `${baseUrl}/results/${fileName}`
 }
 
-async function getModel(id: string, baseUrl: string) {
+async function getModel(id: string, source:IngredientSource, baseUrl: string) {
     let model: Model;
+    const mid = source.model? parseInt(source.model) : 0;
     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())[mid]
     } else {
         const pdb = await getFromCellPackDB(id, baseUrl)
-        model = (await trajectoryFromPDB(pdb).run())[0]
+        model = (await trajectoryFromPDB(pdb).run())[mid]
     }
     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()
     }
-
-    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)
-
+    if (source.selection){
+        const asymIds:string[] = source.selection.split(":")
+        const query = MS.struct.modifier.union([
+            MS.struct.generator.atomGroups({
+                'chain-test': MS.core.set.has([MS.set(...asymIds), MS.ammp('label_asym_id')])
+            })
+        ])
+        const compiled = compile<StructureSelection>(query)
+        const result = compiled(new QueryContext(structure))
+        structure = StructureSelection.unionStructure(result)
+    }
+    else {
+        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)
+    }
     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)
@@ -76,13 +89,25 @@ 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;
+    console.log(ingredient.radii);
+    console.log(segmentLength);
 
     for (let i = 0; i < n; ++i) {
         const cname = `curve${i}`
@@ -97,7 +122,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)
     }
 
@@ -212,7 +237,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 => {
@@ -222,7 +247,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) {
@@ -237,14 +262,53 @@ async function getIngredientStructure(ingredient: Ingredient, baseUrl: string) {
 
     if (source.pdb === 'None') return
 
-    const model = await getModel(source.pdb || name, baseUrl)
+    const model = await getModel(source.pdb || name,source, baseUrl)
     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 = computeStructureBoundary(structure)
+            let modelCenter:Vec3 = Vec3.zero()
+            Vec3.scale(modelCenter,boundary.sphere.center,-1.0);//Model.getCenter(structure.models[0])
+            const m1: Mat4 = Mat4.identity()
+            Mat4.setTranslation(m1, modelCenter)
+            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.create(0, 0, 1)))
+                {            
+                    const q: Quat = Quat.identity();
+                    Quat.rotationTo(q,ingredient.principalAxis,Vec3.create(0, 0, 1))
+                    const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q)
+                    structure = Structure.transform(structure,m);
+                }
+            }
+        }
+        return getAssembly(getResultTransforms(results,legacy), structure)
     }
 }
 
@@ -373,6 +437,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' }),
@@ -399,4 +464,4 @@ export const LoadCellPackModel = StateAction.build({
     } else {
         await loadPackings(ctx, taskCtx, state, params)
     }
-}));
+}));