Quellcode durchsuchen

binary model loading support, latest mycoplasma model.

ludovic autin vor 3 Jahren
Ursprung
Commit
ca825d720e

+ 2 - 2
README.md

@@ -122,9 +122,9 @@ and navigate to `build/viewer`
 
 **Convert any CIF to BinaryCIF**
 
-    node lib/servers/model/preprocess -i file.cif -ob file.bcif
+    node lib/commonjs/servers/model/preprocess -i file.cif -ob file.bcif
 
-To see all available commands, use ``node lib/servers/model/preprocess -h``.
+To see all available commands, use ``node lib/commonjs/servers/model/preprocess -h``.
 
 Or
 

+ 10 - 0
src/extensions/cellpack/data.ts

@@ -15,6 +15,7 @@ export interface CellPacking {
     name: string,
     location: 'surface' | 'interior' | 'cytoplasme',
     ingredients: Packing['ingredients']
+    mb?: Membrane
 }
 
 //
@@ -23,6 +24,7 @@ export interface Cell {
     recipe: Recipe
     cytoplasme?: Packing
     compartments?: { [key: string]: Compartment }
+    mapping_ids?: { [key: number]: [number,string] }
 }
 
 export interface Recipe {
@@ -35,6 +37,12 @@ export interface Recipe {
 export interface Compartment {
     surface?: Packing
     interior?: Packing
+    mb?: Membrane
+}
+
+export interface Membrane{
+    positions: number[];
+    radii: number[];
 }
 
 export interface Packing {
@@ -64,11 +72,13 @@ export interface Ingredient {
     [curveX: string]: unknown;
     /** the orientation in the membrane */
     principalAxis?: Vec3;
+    principalVector?: Vec3;
     /** offset along membrane */
     offset?: Vec3;
     ingtype?: string;
     color?: Vec3;
     confidence?: number;
+    Type?: string;
 }
 
 export interface IngredientSource {

+ 142 - 56
src/extensions/cellpack/model.ts

@@ -8,7 +8,7 @@ import { StateAction, StateBuilder, StateTransformer, State } from '../../mol-st
 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, IngredientSource, CellPacking } from './data';
+import { Ingredient, CellPacking } from './data';
 import { getFromPdb, getFromCellPackDB, IngredientFiles, parseCif, parsePDBfile, getStructureMean, getFromOPM } from './util';
 import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit, Trajectory } from '../../mol-model/structure';
 import { trajectoryFromMmCIF, MmcifFormat } from '../../mol-model-formats/structure/mmcif';
@@ -17,7 +17,7 @@ import { Mat4, Vec3, Quat } from '../../mol-math/linear-algebra';
 import { SymmetryOperator } from '../../mol-math/geometry';
 import { Task, RuntimeContext } from '../../mol-task';
 import { StateTransforms } from '../../mol-plugin-state/transforms';
-import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl, StructureFromAssemblies } from './state';
+import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl, StructureFromAssemblies, CreateSphere } from './state';
 import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
 import { getMatFromResamplePoints } from './curve';
 import { compile } from '../../mol-script/runtime/query/compiler';
@@ -30,6 +30,7 @@ import { Asset } from '../../mol-util/assets';
 import { Color } from '../../mol-util/color';
 import { readFromFile } from '../../mol-util/data-source';
 import { objectForEach } from '../../mol-util/object';
+//import fetch from 'node-fetch';
 
 function getCellPackModelUrl(fileName: string, baseUrl: string) {
     return `${baseUrl}/results/${fileName}`;
@@ -41,10 +42,14 @@ class TrajectoryCache {
     get(id: string) { return this.map.get(id); }
 }
 
-async function getModel(plugin: PluginContext, id: string, ingredient: Ingredient, baseUrl: string, trajCache: TrajectoryCache, file?: Asset.File) {
+async function getModel(plugin: PluginContext, id: string, ingredient: Ingredient, 
+                        baseUrl: string, trajCache: TrajectoryCache, location: string,
+                        file?: Asset.File 
+                        ) {
     const assetManager = plugin.managers.asset;
     const modelIndex = (ingredient.source.model) ? parseInt(ingredient.source.model) : 0;
-    const surface = (ingredient.ingtype) ? (ingredient.ingtype === 'transmembrane') : false;
+    let surface = (ingredient.ingtype) ? (ingredient.ingtype === 'transmembrane') : false;
+    if (location == 'surface') surface = true;
     let trajectory = trajCache.get(id);
     let assets: Asset.Wrapper[] = [];
     if (!trajectory) {
@@ -72,6 +77,7 @@ async function getModel(plugin: PluginContext, id: string, ingredient: Ingredien
                 try {
                     const data = await getFromOPM(plugin, id, assetManager);
                     assets.push(data.asset);
+                    data.pdb.id! = id.toUpperCase();
                     trajectory = await plugin.runTask(trajectoryFromPDB(data.pdb));
                 } catch (e) {
                     // fallback to getFromPdb
@@ -100,34 +106,44 @@ async function getModel(plugin: PluginContext, id: string, ingredient: Ingredien
     return { model, assets };
 }
 
-async function getStructure(plugin: PluginContext, model: Model, source: IngredientSource, props: { assembly?: string } = {}) {
+async function getStructure(plugin: PluginContext, model: Model, source: Ingredient, props: { assembly?: string } = {}) {
     let structure = Structure.ofModel(model);
+    //const label = { label: 'Model', description: Structure.elementDescription(base) };
+    //let structure = new PSO.Molecule.Structure(base, label);
     const { assembly } = props;
 
     if (assembly) {
         structure = await plugin.runTask(StructureSymmetry.buildAssembly(structure, assembly));
     }
     let query;
-    if (source.selection){
-        const asymIds: string[] = source.selection.replace(' ', '').replace(':', '').split('or');
+    if (source.source.selection){
+        var sel: any = source.source.selection;
+        //selection can have the model ID as well. remove it
+        const asymIds: string[] = sel.replaceAll(' ', '').replaceAll(':', '').split('or').slice(1);
+        //console.log("selection is ", source.selection, asymIds);
+        //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')])
+        //    })
+        //]);
         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 compiled = compile<StructureSelection>(query);
     const result = compiled(new QueryContext(structure));
     structure = StructureSelection.unionStructure(result);
-
+    //change here if possible the label or the?  
+    //structure.label =  source.name; 
     return structure;
 }
 
@@ -141,9 +157,9 @@ function getTransformLegacy(trans: Vec3, rot: Quat) {
 }
 
 function getTransform(trans: Vec3, rot: Quat) {
-    const q: Quat = Quat.create(rot[0], rot[1], rot[2], rot[3]);
+    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]);
+    const p: Vec3 = Vec3.create(-trans[0], trans[1], trans[2]);
     Mat4.setTranslation(m, p);
     return m;
 }
@@ -168,7 +184,7 @@ function getCurveTransforms(ingredient: Ingredient) {
     for (let i = 0; i < n; ++i) {
         const cname = `curve${i}`;
         if (!(cname in ingredient)) {
-            // console.warn(`Expected '${cname}' in ingredient`)
+            console.warn(`Expected '${cname}' in ingredient`)
             continue;
         }
         const _points = ingredient[cname] as Vec3[];
@@ -190,8 +206,8 @@ function getCurveTransforms(ingredient: Ingredient) {
     return instances;
 }
 
-function getAssembly(transforms: Mat4[], structure: Structure) {
-    const builder = Structure.Builder();
+function getAssembly(name: string, transforms: Mat4[], structure: Structure) {
+    const builder = Structure.Builder({ label: name });
     const { units } = structure;
 
     for (let i = 0, il = transforms.length; i < il; ++i) {
@@ -307,13 +323,13 @@ async function getCurve(plugin: PluginContext, name: string, ingredient: Ingredi
     });
 
     const curveModel = await plugin.runTask(curveModelTask);
-    return getStructure(plugin, curveModel, ingredient.source);
+    //ingredient.source.selection = undefined;
+    return getStructure(plugin, curveModel, ingredient);
 }
 
-async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles, trajCache: TrajectoryCache) {
+async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles, trajCache: TrajectoryCache, location: 'surface' | 'interior' | 'cytoplasme') {
     const { name, source, results, nbCurve } = ingredient;
     if (source.pdb === 'None') return;
-
     const file = ingredientFiles[source.pdb];
     if (!file) {
         // TODO can these be added to the library?
@@ -325,13 +341,15 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
     }
 
     // model id in case structure is NMR
-    const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, trajCache, file);
+    const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, trajCache, location, file);
     if (!model) return;
-
     let structure: Structure;
     if (nbCurve) {
+        //console.log("await getCurve", name, nbCurve, model);
         structure = await getCurve(plugin, name, ingredient, getCurveTransforms(ingredient), model);
+        //console.log("getCurve", structure);
     } else {
+        if ( (!results || results.length===0)) return;
         let bu: string|undefined = source.bu ? source.bu : undefined;
         if (bu){
             if (bu === 'AU') {
@@ -340,10 +358,14 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
                 bu = bu.slice(2);
             }
         }
-        structure = await getStructure(plugin, model, source, { assembly: bu });
+        structure = await getStructure(plugin, model, ingredient, { assembly: bu });
         // transform with offset and pcp
         let legacy: boolean = true;
-        if (ingredient.offset || ingredient.principalAxis){
+        //if (name === 'MG_213_214_298_6MER_ADP') {
+        //    console.log("getStructure ", ingredient.offset,ingredient.principalVector,ingredient);
+        //}
+        var pcp = ingredient.principalVector?ingredient.principalVector:ingredient.principalAxis;
+        if (pcp){
             legacy = false;
             const structureMean = getStructureMean(structure);
             Vec3.negate(structureMean, structureMean);
@@ -351,22 +373,38 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
             Mat4.setTranslation(m1, structureMean);
             structure = Structure.transform(structure, m1);
             if (ingredient.offset){
-                if (!Vec3.exactEquals(ingredient.offset, Vec3.zero())){
+                let o: Vec3 = Vec3.create(ingredient.offset[0], ingredient.offset[1], ingredient.offset[2]);
+                if (!Vec3.exactEquals(o, Vec3.zero())){ // -1, 1, 4e-16 ??
+                    if (location !== 'surface')//(name === 'MG_213_214_298_6MER_ADP') 
+                    {
+                        Vec3.negate(o, o);
+                        //console.log("after negate offset ",name, o);
+                    }
                     const m: Mat4 = Mat4.identity();
-                    Mat4.setTranslation(m, ingredient.offset);
+                    Mat4.setTranslation(m, o);
                     structure = Structure.transform(structure, m);
                 }
             }
-            if (ingredient.principalAxis){
-                if (!Vec3.exactEquals(ingredient.principalAxis, Vec3.unitZ)){
+            if (pcp){
+                let p: Vec3 = Vec3.create(pcp[0], pcp[1], pcp[2]);
+                if (!Vec3.exactEquals(p, Vec3.unitZ)){
+                    //if (location !== 'surface')//(name === 'MG_213_214_298_6MER_ADP') 
+                    //{
+                        //Vec3.negate(p, p);
+                        //console.log("after negate ", p);
+                    // }
                     const q: Quat = Quat.identity();
-                    Quat.rotationTo(q, ingredient.principalAxis, Vec3.unitZ);
+                    Quat.rotationTo(q, p, Vec3.unitZ);
                     const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
+                    //if (location !== 'surface') Mat4.invert(m, m);
                     structure = Structure.transform(structure, m);
+                    //if (location === 'surface') console.log('surface',name,ingredient.principalVector, q);
                 }
             }
         }
-        structure = getAssembly(getResultTransforms(results, legacy), structure);
+        
+        structure = getAssembly(name, getResultTransforms(results, legacy), structure);
+        //console.log("getStructure ", name, structure.label, structure);
     }
 
     return { structure, assets };
@@ -374,15 +412,15 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
 
 export function createStructureFromCellPack(plugin: PluginContext, packing: CellPacking, baseUrl: string, ingredientFiles: IngredientFiles) {
     return Task.create('Create Packing Structure', async ctx => {
-        const { ingredients, name } = packing;
+        const { ingredients, location, name } = packing;
         const assets: Asset.Wrapper[] = [];
         const trajCache = new TrajectoryCache();
         const structures: Structure[] = [];
         const colors: Color[] = [];
-        let skipColors: boolean = false;
+        //let skipColors: boolean = false;
         for (const iName in ingredients) {
             if (ctx.shouldUpdate) await ctx.update(iName);
-            const ingredientStructure = await getIngredientStructure(plugin, ingredients[iName], baseUrl, ingredientFiles, trajCache);
+            const ingredientStructure = await getIngredientStructure(plugin, ingredients[iName], baseUrl, ingredientFiles, trajCache, location);
             if (ingredientStructure) {
                 structures.push(ingredientStructure.structure);
                 assets.push(...ingredientStructure.assets);
@@ -390,7 +428,8 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
                 if (c){
                     colors.push(Color.fromNormalizedRgb(c[0], c[1], c[2]));
                 } else {
-                    skipColors = true;
+                    colors.push(Color.fromNormalizedRgb(1,0,0));
+                    //skipColors = true;
                 }
             }
         }
@@ -414,21 +453,20 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
         }
 
         if (ctx.shouldUpdate) await ctx.update(`${name} - structure`);
-        const structure = Structure.create(units);
+        const structure = Structure.create(units, {label: name+"."+location});
         for( let i = 0, il = structure.models.length; i < il; ++i) {
             Model.TrajectoryInfo.set(structure.models[i], { size: il, index: i });
         }
-        return { structure, assets, colors: skipColors ? undefined : colors };
+        return { structure, assets, colors: colors };
     });
 }
 
 async function handleHivRna(plugin: PluginContext, packings: CellPacking[], baseUrl: string) {
     for (let i = 0, il = packings.length; i < il; ++i) {
-        if (packings[i].name === 'HIV1_capsid_3j3q_PackInner_0_1_0') {
+        if (packings[i].name === 'HIV1_capsid_3j3q_PackInner_0_1_0'|| packings[i].name === 'HIV_capsid') {
             const url = Asset.getUrlAsset(plugin.managers.asset, `${baseUrl}/extras/rna_allpoints.json`);
             const json = await plugin.runTask(plugin.managers.asset.resolve(url, 'json', false));
             const points = json.data.points as number[];
-
             const curve0: Vec3[] = [];
             for (let j = 0, jl = points.length; j < jl; j += 3) {
                 curve0.push(Vec3.fromArray(Vec3(), points, j));
@@ -477,30 +515,58 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
         const url = Asset.getUrlAsset(plugin.managers.asset, `${params.baseUrl}/membranes/${name}.bcif`);
         b = b.apply(StateTransforms.Data.Download, { url, isBinary: true, label: name }, { state: { isGhost: true } });
     }
-
-    const membrane = await b.apply(StateTransforms.Data.ParseCif, undefined, { state: { isGhost: true } })
+    const props = {
+        type: {
+            name: 'assembly' as const,
+            params: { id: '1' }
+        } 
+    };
+    if (params.source.name === 'id' && params.source.params !== "MycoplasmaGenitalium.json")
+    //old membrane
+    {
+        const membrane = await b.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(StructureFromAssemblies, undefined, { state: { isGhost: true } })
         .commit({ revertOnError: true });
-
-    const membraneParams = {
-        representation: params.preset.representation,
-    };
-
-    await CellpackMembranePreset.apply(membrane, membraneParams, plugin);
+        const membraneParams = {
+            representation: params.preset.representation,
+        };
+        await CellpackMembranePreset.apply(membrane, membraneParams, plugin);        
+    } else {
+        const membrane = await b.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, props, { state: { isGhost: true } })
+        .commit({ revertOnError: true });     
+        const membraneParams = {
+            representation: params.preset.representation,
+        };
+        await CellpackMembranePreset.apply(membrane, membraneParams, plugin);           
+    }  
 }
 
 async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, state: State, params: LoadCellPackModelParams) {
     const ingredientFiles = params.ingredients || [];
 
     let cellPackJson: StateBuilder.To<PSO.Format.Json, StateTransformer<PSO.Data.String, PSO.Format.Json>>;
+    let modelFile: Asset.File|null= params.results;
     if (params.source.name === 'id') {
         const url = Asset.getUrlAsset(plugin.managers.asset, getCellPackModelUrl(params.source.params, params.baseUrl));
+        //console.log("getting "+params.source.params+" "+url.url);
         cellPackJson = state.build().toRoot()
             .apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.source.params }, { state: { isGhost: true } });
+
+        if (params.source.params === "MycoplasmaGenitalium.json"){
+            const m_url = Asset.getUrlAsset(plugin.managers.asset, `${params.baseUrl}/results/results_149_curated_serialized.bin`);
+            //console.log("getting results "+m_url.url);
+            const model_data = await fetch(m_url.url);
+            modelFile = Asset.File(new File([await model_data.arrayBuffer()], 'model.bin'));
+            //console.log("MycoplasmaGenitalium.json loading setup ?",modelFile);
+        }
     } else {
         const file = params.source.params;
+        const rfile = params.results;
         if (!file?.file) {
             plugin.log.error('No file selected');
             return;
@@ -510,25 +576,29 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
         if (file.name.toLowerCase().endsWith('.zip')) {
             const data = await readFromFile(file.file, 'zip').runInContext(runtime);
             jsonFile = Asset.File(new File([data['model.json']], 'model.json'));
+            modelFile = Asset.File(new File([data['model.bin']], 'model.bin'));
             objectForEach(data, (v, k) => {
                 if (k === 'model.json') return;
+                else if (k === 'model.bin') return;
                 ingredientFiles.push(Asset.File(new File([v], k)));
             });
         } else {
             jsonFile = file;
+            modelFile = rfile;
         }
-
         cellPackJson = state.build().toRoot()
             .apply(StateTransforms.Data.ReadFile, { file: jsonFile, isBinary: false, label: jsonFile.name }, { state: { isGhost: true } });
+        
     }
 
     const cellPackBuilder = cellPackJson
         .apply(StateTransforms.Data.ParseJson, undefined, { state: { isGhost: true } })
-        .apply(ParseCellPack);
-
+        .apply(ParseCellPack,{modeFile:modelFile});
+        
     const cellPackObject = await state.updateTree(cellPackBuilder).runInContext(runtime);
+    
     const { packings } = cellPackObject.obj!.data;
-
+    
     await handleHivRna(plugin, packings, params.baseUrl);
 
     for (let i = 0, il = packings.length; i < il; ++i) {
@@ -544,8 +614,22 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
             representation: params.preset.representation,
         };
         await CellpackPackingPreset.apply(packing, packingParams, plugin);
-        if ( packings[i].location === 'surface' && params.membrane){
-            await loadMembrane(plugin, packings[i].name, state, params);
+        if ( packings[i].location === 'surface') {
+            if (params.membrane){
+                await loadMembrane(plugin, packings[i].name, state, params);
+            }
+            if (typeof(packings[i].mb) !== 'undefined'){
+                var nSpheres =  packings[i].mb!.positions.length/3;
+                for (var j=0;j<nSpheres;j++) {
+                    await state.build()
+                    .toRoot()
+                    .apply(CreateSphere, {center:Vec3.create(packings[i].mb!.positions[j*3+0],
+                                                            packings[i].mb!.positions[j*3+1],
+                                                            packings[i].mb!.positions[j*3+2]),
+                                                            radius:packings[i].mb!.radii[j] })
+                    .commit()
+                }
+            }
         }
     }
 }
@@ -556,16 +640,18 @@ const LoadCellPackModelParams = {
             ['blood_hiv_immature_inside.json', 'Blood HIV immature'],
             ['HIV_immature_model.json', 'HIV immature'],
             ['BloodHIV1.0_mixed_fixed_nc1.cpr', 'Blood HIV'],
-            ['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV'],
+            ['HIV-1_0.1.6-8_mixed_radii_pdb.json', 'HIV'],
             ['influenza_model1.json', 'Influenza envelope'],
             ['InfluenzaModel2.json', 'Influenza Complete'],
             ['ExosomeModel.json', 'Exosome Model'],
-            ['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma simple'],
-            ['MycoplasmaModel.json', 'Mycoplasma WholeCell model'],
+            //['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma simple'],
+            //['MycoplasmaModel.json', 'Mycoplasma WholeCell model'],
+            ['MycoplasmaGenitalium.json', 'Mycoplasma Genitalium curated model'],
         ] as const, { description: 'Download the model definition with `id` from the server at `baseUrl.`' }),
         'file': PD.File({ accept: '.json,.cpr,.zip', description: 'Open model definition from .json/.cpr file or open .zip file containing model definition plus ingredients.' }),
     }, { options: [['id', 'Id'], ['file', 'File']] }),
     baseUrl: PD.Text(DefaultCellPackBaseUrl),
+    results : PD.File({ accept: '.bin,.json' }),
     membrane: PD.Boolean(true),
     ingredients: PD.FileList({ accept: '.cif,.bcif,.pdb', label: 'Ingredients' }),
     preset: PD.Group({
@@ -581,4 +667,4 @@ export const LoadCellPackModel = StateAction.build({
     from: PSO.Root
 })(({ state, params }, ctx: PluginContext) => Task.create('CellPack Loader', async taskCtx => {
     await loadPackings(ctx, taskCtx, state, params);
-}));
+}));

+ 6 - 5
src/extensions/cellpack/preset.ts

@@ -9,8 +9,8 @@ import { StructureRepresentationPresetProvider, presetStaticComponent } from '..
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ColorNames } from '../../mol-util/color/names';
 import { CellPackGenerateColorThemeProvider } from './color/generate';
-import { CellPackInfoProvider } from './property';
-import { CellPackProvidedColorThemeProvider } from './color/provided';
+//import { CellPackInfoProvider } from './property';
+//import { CellPackProvidedColorThemeProvider } from './color/provided';
 
 export const CellpackPackingPresetParams = {
     traceOnly: PD.Boolean(true),
@@ -42,8 +42,9 @@ export const CellpackPackingPreset = StructureRepresentationPresetProvider({
             Object.assign(reprProps, { sizeFactor: 2 });
         }
 
-        const info = structureCell.obj?.data && CellPackInfoProvider.get(structureCell.obj?.data).value;
-        const color = info?.colors ? CellPackProvidedColorThemeProvider.name : CellPackGenerateColorThemeProvider.name;
+        //const info = structureCell.obj?.data && CellPackInfoProvider.get(structureCell.obj?.data).value;
+        //default is generated
+        const color = CellPackGenerateColorThemeProvider.name;//info?.colors ? CellPackProvidedColorThemeProvider.name : CellPackGenerateColorThemeProvider.name;
 
         const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, {});
         const representations = {
@@ -92,4 +93,4 @@ export const CellpackMembranePreset = StructureRepresentationPresetProvider({
 
         return { components, representations };
     }
-});
+});

+ 1 - 1
src/extensions/cellpack/property.ts

@@ -34,4 +34,4 @@ export const CellPackInfoProvider: CustomStructureProperty.Provider<typeof CellP
             value: { ...CellPackInfoParams.info.defaultValue, ...props.info }
         };
     }
-});
+});

+ 84 - 0
src/extensions/cellpack/representation.ts

@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { ShapeRepresentation } from '../../mol-repr/shape/representation';
+import { Shape } from '../../mol-model/shape';
+import { ColorNames } from '../../mol-util/color/names';
+import { RuntimeContext } from '../../mol-task';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
+import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
+import { Polyhedron, DefaultPolyhedronProps } from '../../mol-geo/primitive/polyhedron';
+import { Icosahedron } from '../../mol-geo/primitive/Icosahedron';
+import { Mat4, Vec3 } from '../../mol-math/linear-algebra';
+import { RepresentationParamsGetter, Representation, RepresentationContext } from '../../mol-repr/representation';
+
+
+interface MembraneSphereData {
+    radius: number
+    center: Vec3
+}
+
+
+const MembraneSphereParams = {
+    ...Mesh.Params,
+    cellColor: PD.Color(ColorNames.orange),
+    cellScale: PD.Numeric(2, { min: 0.1, max: 5, step: 0.1 }),
+    radius: PD.Numeric(2, { min: 0.1, max: 5, step: 0.1 }),
+    center: PD.Vec3(Vec3.create(0,0,0)),
+    quality: { ...Mesh.Params.quality, isEssential: false },
+};
+
+type MeshParams = typeof MembraneSphereParams
+
+const MembraneSphereVisuals = {
+    'mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<MembraneSphereData, MeshParams>) => ShapeRepresentation(getMBShape, Mesh.Utils),
+};
+
+export const MBParams = {
+    ...MembraneSphereParams
+};
+export type MBParams = typeof MBParams
+export type UnitcellProps = PD.Values<MBParams>
+
+function getMBMesh(data: MembraneSphereData, props: UnitcellProps, mesh?: Mesh) {
+    const state = MeshBuilder.createState(256, 128, mesh);
+    const radius = props.radius;
+    var p = DefaultPolyhedronProps;
+    p.detail = 3;
+    p.radius = radius;
+    const { vertices, indices } = Icosahedron();
+    const asphere = Polyhedron(vertices, indices, p);
+    //const asphere = Sphere(3);
+    var trans:Mat4 = Mat4.identity();
+    //Mat4.fromScaling(trans, Vec3.create(radius,radius,radius));
+    state.currentGroup = 1;
+    MeshBuilder.addPrimitive(state, trans, asphere);
+    const m = MeshBuilder.getMesh(state);
+    return m;
+}
+
+function getMBShape(ctx: RuntimeContext, data: MembraneSphereData, props: UnitcellProps, shape?: Shape<Mesh>) {
+    const geo = getMBMesh(data, props, shape && shape.geometry);
+    const label = "mb";
+    return Shape.create(label, data, geo, () => props.cellColor, () => 1, () => label);
+}
+
+//
+/*
+export function getMBData(model: Model, symmetry: Symmetry, props: UnitcellProps) {
+    const ref = Vec3();
+    if (props.ref === 'model') {
+        Vec3.transformMat4(ref, Model.getCenter(model), symmetry.spacegroup.cell.toFractional);
+    }
+    return { symmetry, ref };
+}
+*/
+
+export type MBRepresentation = Representation<MembraneSphereData, MBParams>
+export function MBRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<MembraneSphereData, MBParams>): MBRepresentation {
+    return Representation.createMulti('MB', ctx, getParams, Representation.StateBuilder, MembraneSphereVisuals as unknown as Representation.Def<MembraneSphereData, MBParams>);
+}

+ 196 - 13
src/extensions/cellpack/state.ts

@@ -9,15 +9,19 @@ 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';
+import { IngredientFiles, getFloatValue } from './util';
 import { Asset } from '../../mol-util/assets';
 import { PluginContext } from '../../mol-plugin/context';
 import { CellPackInfoProvider } from './property';
 import { Structure, StructureSymmetry, Unit, Model } from '../../mol-model/structure';
 import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
+import { Vec3, Quat } from '../../mol-math/linear-algebra';
+import { readFromFile } from '../../mol-util/data-source';
+import { StateTransformer } from '../../mol-state';
+import { MBRepresentation, MBParams } from './representation';
 
-export const DefaultCellPackBaseUrl = 'https://mesoscope.scripps.edu/data/cellPACK_data/cellPACK_database_1.1.0/';
-
+//export const DefaultCellPackBaseUrl = 'https://mesoscope.scripps.edu/data/cellPACK_data/cellPACK_database_1.1.0/';
+export const DefaultCellPackBaseUrl = 'https://raw.githubusercontent.com/mesoscope/cellPACK_data/master/cellPACK_database_1.1.0/'
 export class CellPack extends PSO.Create<_CellPack>({ name: 'CellPack', typeClass: 'Object' }) { }
 
 export { ParseCellPack };
@@ -26,23 +30,178 @@ const ParseCellPack = PluginStateTransform.BuiltIn({
     name: 'parse-cellpack',
     display: { name: 'Parse CellPack', description: 'Parse CellPack from JSON data' },
     from: PSO.Format.Json,
-    to: CellPack
+    to: CellPack,
+    params: a => {
+        return {
+            modeFile: PD.File({ accept: '.bin' })
+        };
+    }
 })({
-    apply({ a }) {
+    apply({a, params}) {
         return Task.create('Parse CellPack', async ctx => {
             const cell = a.data as Cell;
-
+            let counter_id = 0;
+            let fiber_counter_id = 0;
+            let comp_counter = 0;
             const packings: CellPacking[] = [];
             const { compartments, cytoplasme } = cell;
+            let iName = "";
+            if(!cell.mapping_ids)cell.mapping_ids={};
+            if (cytoplasme) {
+                packings.push({ name: 'Cytoplasme', location: 'cytoplasme', ingredients: cytoplasme.ingredients });
+                for (iName in cytoplasme.ingredients){
+                    if (cytoplasme.ingredients[iName].ingtype == 'fiber') {
+                        cell.mapping_ids[-(fiber_counter_id+1)]=[comp_counter,iName];
+                        if (!cytoplasme.ingredients[iName].nbCurve) cytoplasme.ingredients[iName].nbCurve = 0;
+                        fiber_counter_id++;                        
+                    }
+                    else {
+                        cell.mapping_ids[counter_id]=[comp_counter,iName];
+                        if (!cytoplasme.ingredients[iName].results) {cytoplasme.ingredients[iName].results=[]}
+                        counter_id++;
+                    }
+                }
+                comp_counter++;
+            }
             if (compartments) {
                 for (const name in compartments) {
                     const { surface, interior } = compartments[name];
-                    if (surface) packings.push({ name, location: 'surface', ingredients: surface.ingredients });
-                    if (interior) packings.push({ name, location: 'interior', ingredients: interior.ingredients });
+                    if (surface) {
+                        packings.push({ name, location: 'surface', ingredients: surface.ingredients, mb: compartments[name].mb });
+                        for (iName in surface.ingredients){
+                            if (surface.ingredients[iName].ingtype == 'fiber') {
+                                cell.mapping_ids[-(fiber_counter_id+1)]=[comp_counter,iName];
+                                if (!surface.ingredients[iName].nbCurve) surface.ingredients[iName].nbCurve = 0;
+                                fiber_counter_id++;                        
+                            }
+                            else {
+                                cell.mapping_ids[counter_id]=[comp_counter,iName];
+                                if (!surface.ingredients[iName].results) {surface.ingredients[iName].results=[]}
+                                counter_id++;
+                            }
+                        }  
+                        comp_counter++;                  
+                    }
+                    if (interior) {
+                        packings.push({ name, location: 'interior', ingredients: interior.ingredients });
+                        for (iName in interior.ingredients){
+                            if (interior.ingredients[iName].ingtype == 'fiber') {
+                                cell.mapping_ids[-(fiber_counter_id+1)]=[comp_counter,iName];
+                                if (!interior.ingredients[iName].nbCurve) interior.ingredients[iName].nbCurve = 0;
+                                fiber_counter_id++;                        
+                            }
+                            else {
+                                cell.mapping_ids[counter_id]=[comp_counter,iName];
+                                if (!interior.ingredients[iName].results) {interior.ingredients[iName].results=[]}
+                                counter_id++;
+                            }
+                        }  
+                        comp_counter++;                  
+                    }
                 }
             }
-            if (cytoplasme) packings.push({ name: 'Cytoplasme', location: 'cytoplasme', ingredients: cytoplasme.ingredients });
-
+            if (params.modeFile && params.modeFile.file){
+                const model_data = await readFromFile(params.modeFile.file, 'binary').runInContext(ctx);//async ?
+                var numbers = new DataView(model_data.buffer);
+                var ninst = getFloatValue(numbers,0);
+                var npoints = getFloatValue(numbers,4);
+                var ncurve = getFloatValue(numbers,8);
+                /*
+                console.log("Parse CellPack");
+                console.log("ninst ",ninst);
+                console.log("npoints ",npoints);
+                console.log("ncurve ",ncurve);
+                */
+                let pos = new Float32Array();
+                let quat = new Float32Array();
+                let ctr_pos = new Float32Array();
+                //let ctr_norm = new Float32Array();
+                let ctr_info = new Float32Array();
+                let curve_ids = new Float32Array();                                
+                //let ctrl_pts= new Float32Array();
+                //let ctrl_normal= new Float32Array();
+                //let ctrl_info= new Float32Array();//#curve_id, curve_type, angle, uLength
+                let offset = 12;
+                if (ninst !== 0){
+                    pos = new Float32Array(model_data.buffer,offset,ninst*4);offset+=ninst*4*4;
+                    quat = new Float32Array(model_data.buffer,offset,ninst*4);offset+=ninst*4*4;
+                }
+                if ( npoints != 0 )
+                {
+                    ctr_pos = new Float32Array(model_data.buffer,offset,npoints*4);offset+=npoints*4*4;
+                    //ctr_norm = new Float32Array(model_data,offset,ncurve*4);
+                    offset+=npoints*4*4;
+                    ctr_info = new Float32Array(model_data.buffer,offset,npoints*4);offset+=npoints*4*4;
+                    curve_ids = new Float32Array(model_data.buffer,offset,ncurve*4);offset+=ncurve*4*4;
+                }
+                //data_from_buffer = {"pos":pos,"quat":quat,"ctrl_pts":ctrl_pts,"ctrl_normal":ctrl_normal,"ctrl_info":ctrl_info};
+                //create all the bodys then the instance ?
+                for (var i=0;i<ninst;i++)
+                {
+                  let x:number =  pos[i*4+0];
+                  let y:number =  pos[i*4+1];
+                  let z:number =  pos[i*4+2];
+                  //q = new THREE.Quaternion(quats[i*4+0],quats[i*4+1],quats[i*4+2],quats[i*4+3]);
+                  let ingr_id = pos[i*4+3] as number;
+                  let pid = cell.mapping_ids[ingr_id];
+                  if (!packings[pid[0]].ingredients[pid[1]].results) {
+                    packings[pid[0]].ingredients[pid[1]].results=[];
+                  }
+                  packings[pid[0]].ingredients[pid[1]].results.push([Vec3.create(x,y,z), 
+                                                                     Quat.create(quat[i*4+0],quat[i*4+1],quat[i*4+2],quat[i*4+3])]);
+                }
+                let counter = 0;
+                let ctr_points:Vec3[] = [];
+                let prev_ctype = 0;
+                let prev_cid = 0;
+                /*
+                console.log("ctr_info",ctr_info);
+                console.log("curve_ids",curve_ids);
+                */
+                for (var i=0;i<npoints;i++)
+                {
+                    let x:number = -ctr_pos[i*4+0];
+                    let y:number =  ctr_pos[i*4+1];
+                    let z:number =  ctr_pos[i*4+2];
+                    let cid:number = ctr_info[i*4+0];//curve id
+                    let ctype:number = curve_ids[cid*4+0];//curve type  
+                    //cid  148 165 -1 0
+                    //console.log("cid ",cid,ctype,prev_cid,prev_ctype);//165,148
+                    if (prev_ctype != ctype){
+                        let pid = cell.mapping_ids[-prev_ctype-1];
+                        const cname = `curve${counter}`;
+                        //console.log("ctype ",pid,cname,prev_ctype,(-prev_ctype-1));
+                        packings[pid[0]].ingredients[pid[1]].nbCurve = counter+1;
+                        packings[pid[0]].ingredients[pid[1]][cname] = ctr_points;
+                        ctr_points = [];
+                        counter = 0;
+                    }
+                    else if (prev_cid != cid){
+                        ctr_points = [];
+                        let pid = cell.mapping_ids[-prev_ctype-1];
+                        const cname = `curve${counter}`;
+                        //console.log("cid ",pid,cname,prev_ctype,(-prev_ctype-1),prev_cid,cid);
+                        packings[pid[0]].ingredients[pid[1]][cname] = ctr_points;            
+                        counter+=1;
+                    }
+                    ctr_points.push(Vec3.create(x,y,z));
+                    prev_ctype = ctype;
+                    prev_cid = cid;
+                }
+                //do the last one
+                if ( npoints != 0 )
+                {
+                    let pid = cell.mapping_ids[-prev_ctype-1];
+                    const cname = `curve${counter}`;
+                    //console.log("ctype ",pid,cname,prev_ctype,(-prev_ctype-1));
+                    packings[pid[0]].ingredients[pid[1]].nbCurve = counter+1;
+                    packings[pid[0]].ingredients[pid[1]][cname] = ctr_points;
+                    ctr_points = [];
+                }
+                counter = 0;                
+                //console.log("done");                
+                //console.log(packings);
+            }
             return new CellPack({ cell, packings });
         });
     }
@@ -77,9 +236,8 @@ const StructureFromCellpack = PluginStateTransform.BuiltIn({
             await CellPackInfoProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, structure, {
                 info: { packingsCount: a.data.packings.length, packingIndex: params.packing, colors }
             });
-
             (cache as any).assets = assets;
-            return new PSO.Molecule.Structure(structure, { label: packing.name });
+            return new PSO.Molecule.Structure(structure, { label: packing.name+"."+packing.location });
         });
     },
     dispose({ b, cache }) {
@@ -125,7 +283,7 @@ const StructureFromAssemblies = PluginStateTransform.BuiltIn({
                     const s = await StructureSymmetry.buildAssembly(initial_structure, a.id).runInContext(ctx);
                     structures.push(s);
                 }
-                const builder = Structure.Builder();
+                const builder = Structure.Builder({ label: "Membrane" });
                 let offsetInvariantId = 0;
                 for (const s of structures) {
                     let maxInvariantId = 0;
@@ -148,3 +306,28 @@ const StructureFromAssemblies = PluginStateTransform.BuiltIn({
         b?.data.customPropertyDescriptors.dispose();
     }
 });
+
+
+const CreateTransformer = StateTransformer.builderFactory('cellPACK');
+export const CreateSphere = CreateTransformer({
+    name: 'create-sphere',
+    display: 'Sphere',
+    from: PSO.Root, // or whatever data source
+    to: PSO.Shape.Representation3D,
+    params: {
+        center: PD.Vec3(Vec3()),
+        radius: PD.Numeric(1)
+    }
+})({
+    canAutoUpdate({ oldParams, newParams }) {
+        return true;
+    },
+    apply({ a, params }, plugin: PluginContext) {
+        return Task.create('Custom Sphere', async ctx => {
+            const data = params;
+            const repr = MBRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes },  () => (MBParams));
+            await repr.createOrUpdate({ ...params, quality: 'custom',doubleSided:true }, data).runInContext(ctx);
+            return new PSO.Shape.Representation3D({ repr, sourceData: a }, { label: `My Sphere` });
+        });
+    }
+});

+ 34 - 2
src/extensions/cellpack/util.ts

@@ -37,7 +37,8 @@ async function downloadPDB(plugin: PluginContext, url: string, id: string, asset
 }
 
 export async function getFromPdb(plugin: PluginContext, pdbId: string, assetManager: AssetManager) {
-    const { cif, asset } = await downloadCif(plugin, `https://models.rcsb.org/${pdbId.toUpperCase()}.bcif`, true, assetManager);
+    //${pdbId.toUpperCase()}
+    const { cif, asset } = await downloadCif(plugin, `https://models.rcsb.org/${pdbId}.bcif`, true, assetManager);
     return { mmcif: cif.blocks[0], asset };
 }
 
@@ -74,4 +75,35 @@ export function getStructureMean(structure: Structure) {
     }
     const { elementCount } = structure;
     return Vec3.create(xSum / elementCount, ySum / elementCount, zSum / elementCount);
-}
+}
+
+export function getFloatValue(value: DataView, offset : number) {
+  // if the last byte is a negative value (MSB is 1), the final
+  // float should be too
+  const negative = value.getInt8(offset + 2) >>> 31;
+
+  // this is how the bytes are arranged in the byte array/DataView
+  // buffer
+  const [b0, b1, b2, exponent] = [
+      // get first three bytes as unsigned since we only care
+      // about the last 8 bits of 32-bit js number returned by
+      // getUint8().
+      // Should be the same as: getInt8(offset) & -1 >>> 24
+      value.getUint8(offset),
+      value.getUint8(offset + 1),
+      value.getUint8(offset + 2),
+
+      // get the last byte, which is the exponent, as a signed int
+      // since it's already correct
+      value.getInt8(offset + 3)
+  ];
+
+  let mantissa = b0 | (b1 << 8) | (b2 << 16);
+  if (negative) {
+      // need to set the most significant 8 bits to 1's since a js
+      // number is 32 bits but our mantissa is only 24.
+      mantissa |= 255 << 24;
+  }
+
+  return mantissa * Math.pow(10, exponent);
+}