Browse Source

Merge pull request #35 from corredD/forkdev

Forkdev
Alexander Rose 5 years ago
parent
commit
090ad613cc

+ 7 - 2
src/extensions/cellpack/color.ts

@@ -34,7 +34,8 @@ export function CellPackColorTheme(ctx: ThemeDataContext, props: PD.Values<CellP
 
     if (ctx.structure && info) {
         const colors = distinctColors(info.packingsCount);
-        const hcl = Hcl.fromColor(Hcl(), colors[info.packingIndex]);
+        let hcl = Hcl.fromColor(Hcl(), colors[info.packingIndex]);
+
         const hue = [Math.max(0, hcl[0] - 35), Math.min(360, hcl[0] + 35)] as [number, number];
 
         const { models } = ctx.structure.root;
@@ -54,7 +55,11 @@ export function CellPackColorTheme(ctx: ThemeDataContext, props: PD.Values<CellP
         const modelColor = new Map<number, Color>();
         for (let i = 0, il = models.length; i < il; ++i) {
             const idx = models[i].trajectoryInfo.index;
-            modelColor.set(models[i].trajectoryInfo.index, palette.color(idx));
+            let acolor = palette.color(idx);
+            if (info.color){
+                acolor = info.colors[idx];
+            }
+            modelColor.set(models[i].trajectoryInfo.index, acolor);
         }
 
         color = (location: Location): Color => {

+ 8 - 2
src/extensions/cellpack/curve.ts

@@ -191,8 +191,14 @@ function GetMiniFrame(points: Vec3[], normals: Vec3[]) {
 
 const rpTmpVec1 = Vec3();
 
-export function getMatFromResamplePoints(points: NumberArray, segmentLength: number) {
-    const new_points = ResampleControlPoints(points, segmentLength);
+export function getMatFromResamplePoints(points: NumberArray, segmentLength: number, resample: boolean) {
+    let new_points: Vec3[] = [];
+    if (resample) new_points = ResampleControlPoints(points, segmentLength);
+    else {
+        for (let idx = 0; idx < points.length / 3; ++idx){
+            new_points.push(Vec3.fromArray(Vec3.zero(), points, idx * 3));
+        }
+    }
     const npoints = new_points.length;
     const new_normal = GetSmoothNormals(new_points);
     const frames = GetMiniFrame(new_points, new_normal);

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

@@ -59,6 +59,7 @@ export interface Ingredient {
     radii?: [Radii];
     /** Number of `curveX` properties in the object where `X` is a 0-indexed number */
     nbCurve?: number;
+    uLength?: number;
     /** Curve properties are Vec3[] but that is not expressable in TypeScript */
     [curveX: string]: unknown;
     /** the orientation in the membrane */
@@ -66,6 +67,8 @@ export interface Ingredient {
     /** offset along membrane */
     offset?: Vec3;
     ingtype?: string;
+    color?: Vec3;
+    confidence?: number;
 }
 
 export interface IngredientSource {

+ 66 - 30
src/extensions/cellpack/model.ts

@@ -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 } from './state';
+import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl, GetAllAssamblyinOneStructure } from './state';
 import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
 import { getMatFromResamplePoints } from './curve';
 import { compile } from '../../mol-script/runtime/query/compiler';
@@ -27,6 +27,7 @@ import { Column } from '../../mol-data/db';
 import { createModels } from '../../mol-model-formats/structure/basic/parser';
 import { CellpackPackingPreset, CellpackMembranePreset } from './preset';
 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';
 
@@ -108,7 +109,7 @@ async function getStructure(plugin: PluginContext, model: Model, source: Ingredi
     }
     let query;
     if (source.selection){
-        const asymIds: string[] = source.selection.replace(' :', '').split(' or');
+        const asymIds: string[] = source.selection.replace(' ', '').replace(':', '').split('or');
         query = MS.struct.modifier.union([
             MS.struct.generator.atomGroups({
                 'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
@@ -155,11 +156,15 @@ function getResultTransforms(results: Ingredient['results'], legacy: boolean) {
 function getCurveTransforms(ingredient: Ingredient) {
     const n = ingredient.nbCurve || 0;
     const instances: Mat4[] = [];
-    const segmentLength = ingredient.radii
-        ? (ingredient.radii[0].radii
+    let segmentLength = 3.4;
+    if (ingredient.uLength){
+        segmentLength = ingredient.uLength;
+    } else if (ingredient.radii){
+        segmentLength = ingredient.radii[0].radii
             ? ingredient.radii[0].radii[0] * 2.0
-            : 3.4)
-        : 3.4;
+            : 3.4;
+    }
+    let resampling: boolean = false;
     for (let i = 0; i < n; ++i) {
         const cname = `curve${i}`;
         if (!(cname in ingredient)) {
@@ -171,12 +176,17 @@ function getCurveTransforms(ingredient: Ingredient) {
             // TODO handle curve with 2 or less points
             continue;
         }
+        // test for resampling
+        let distance: number = Vec3.distance(_points[0], _points[1]);
+        if (distance >= segmentLength + 2.0) {
+            console.info(distance);
+            resampling = true;
+        }
         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, segmentLength);
+        const newInstances = getMatFromResamplePoints(points, segmentLength, resampling);
         instances.push(...newInstances);
     }
-
     return instances;
 }
 
@@ -290,7 +300,6 @@ function getCifCurve(name: string, transforms: Mat4[], model: Model) {
 
 async function getCurve(plugin: PluginContext, name: string, ingredient: Ingredient, transforms: Mat4[], model: Model) {
     const cif = getCifCurve(name, transforms, model);
-
     const curveModelTask = Task.create('Curve Model', async ctx => {
         const format = MmcifFormat.fromFrame(cif);
         const models = await createModels(format.data.db, format, ctx);
@@ -308,10 +317,10 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
     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 === 'HIV1_CAhex_0_1_0') return; // 1VU4CtoH_hex.pdb
+        if (name === 'HIV1_CAhexCyclophilA_0_1_0') return; // 1AK4fitTo1VU4hex.pdb
+        if (name === 'iLDL') return; // EMD-5239
+        if (name === 'peptides') return; // peptide.pdb
         if (name === 'lypoglycane') return;
     }
 
@@ -369,15 +378,27 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
         const assets: Asset.Wrapper[] = [];
         const trajCache = new TrajectoryCache();
         const structures: Structure[] = [];
+        let colors: Color[] = [];
+        let skip_color: boolean = false;
         for (const iName in ingredients) {
             if (ctx.shouldUpdate) await ctx.update(iName);
             const ingredientStructure = await getIngredientStructure(plugin, ingredients[iName], baseUrl, ingredientFiles, trajCache);
             if (ingredientStructure) {
                 structures.push(ingredientStructure.structure);
                 assets.push(...ingredientStructure.assets);
+                if (ingredients[iName].color){
+                    let c = ingredients[iName].color;
+                    if (!c) c = Vec3.create(1, 0, 0);
+                    colors.push(Color.fromNormalizedRgb(c[0], c[1], c[2]));
+                } else {
+                    colors.push(Color.fromNormalizedRgb(1, 0, 0));
+                    skip_color = true;
+                }
             }
         }
-
+        if (skip_color){
+            colors = [];
+        }
         if (ctx.shouldUpdate) await ctx.update(`${name} - units`);
         const builder = Structure.Builder({ label: name });
         let offsetInvariantId = 0;
@@ -399,7 +420,7 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
             trajectoryInfo.size = il;
             trajectoryInfo.index = i;
         }
-        return { structure, assets };
+        return { structure, assets, colors };
     });
 }
 
@@ -435,11 +456,25 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
                 break;
             }
         }
+        if (!file){
+            // check for cif directly
+            const cifileName = `${name}.cif`;
+            for (const f of params.ingredients.files) {
+                if (cifileName === f.name) {
+                    file = f;
+                    break;
+                }
+            }
+        }
     }
 
     let b = state.build().toRoot();
     if (file) {
-        b = b.apply(StateTransforms.Data.ReadFile, { file, isBinary: true, label: file.name }, { state: { isGhost: true } });
+        if (file.name.endsWith('.cif')) {
+            b = b.apply(StateTransforms.Data.ReadFile, { file, isBinary: false, label: file.name }, { state: { isGhost: true } });
+        } else if (file.name.endsWith('.bcif')) {
+            b = b.apply(StateTransforms.Data.ReadFile, { file, isBinary: true, label: file.name }, { state: { isGhost: true } });
+        }
     } else {
         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 } });
@@ -449,11 +484,13 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
         .apply(StateTransforms.Model.TrajectoryFromMmCif, undefined, { state: { isGhost: true } })
         .apply(StateTransforms.Model.ModelFromTrajectory, undefined, { state: { isGhost: true } })
         .apply(StateTransforms.Model.StructureFromModel)
+        .apply(GetAllAssamblyinOneStructure, undefined, { state: { isGhost: true } })
         .commit({ revertOnError: true });
 
     const membraneParams = {
         representation: params.preset.representation,
     };
+
     await CellpackMembranePreset.apply(membrane, membraneParams, plugin);
 }
 
@@ -510,7 +547,7 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
             representation: params.preset.representation,
         };
         await CellpackPackingPreset.apply(packing, packingParams, plugin);
-        if ( packings[i].location === 'surface' ){
+        if ( packings[i].location === 'surface' && params.membrane){
             await loadMembrane(plugin, packings[i].name, state, params);
         }
     }
@@ -518,18 +555,21 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
 
 const LoadCellPackModelParams = {
     source: PD.MappedStatic('id', {
-        'id': PD.Select('influenza_model1.json', [
-            ['blood_hiv_immature_inside.json', 'blood_hiv_immature_inside'],
-            ['BloodHIV1.0_mixed_fixed_nc1.cpr', 'BloodHIV1.0_mixed_fixed_nc1'],
-            ['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'],
+        'id': PD.Select('InfluenzaModel2.json', [
+            ['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'],
+            ['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'],
         ] 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),
+    membrane: PD.Boolean(true),
     ingredients : PD.Group({
         files: PD.FileList({ accept: '.cif,.bcif,.pdb' })
     }, { isExpanded: true }),
@@ -545,9 +585,5 @@ export const LoadCellPackModel = StateAction.build({
     params: LoadCellPackModelParams,
     from: PSO.Root
 })(({ state, params }, ctx: PluginContext) => Task.create('CellPack Loader', async taskCtx => {
-    if (params.source.name === 'id' && params.source.params === 'hiv_lipids.bcif') {
-        await loadMembrane(ctx, 'hiv_lipids', state, params);
-    } else {
-        await loadPackings(ctx, taskCtx, state, params);
-    }
+    await loadPackings(ctx, taskCtx, state, params);
 }));

+ 1 - 0
src/extensions/cellpack/preset.ts

@@ -85,6 +85,7 @@ export const CellpackMembranePreset = StructureRepresentationPresetProvider({
         };
 
         await update.commit({ revertOnError: true });
+
         return { components, representations };
     }
 });

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

@@ -8,15 +8,18 @@ import { CustomStructureProperty } from '../../mol-model-props/common/custom-str
 import { Structure } from '../../mol-model/structure';
 import { CustomProperty } from '../../mol-model-props/common/custom-property';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { Color } from '../../mol-util/color';
 import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export type CellPackInfoValue = {
     packingsCount: number
     packingIndex: number
+    color: boolean
+    colors: Color[]
 }
 
 const CellPackInfoParams = {
-    info: PD.Value<CellPackInfoValue>({ packingsCount: 1, packingIndex: 0 }, { isHidden: true })
+    info: PD.Value<CellPackInfoValue>({ packingsCount: 1, packingIndex: 0, color: false, colors:[] }, { isHidden: true })
 };
 type CellPackInfoParams = PD.Values<typeof CellPackInfoParams>
 

+ 115 - 4
src/extensions/cellpack/state.ts

@@ -13,6 +13,8 @@ import { IngredientFiles } from './util';
 import { Asset } from '../../mol-util/assets';
 import { PluginContext } from '../../mol-plugin/context';
 import { CellPackInfoProvider } from './property';
+import { Structure, StructureSymmetry, Unit } from '../../mol-model/structure';
+import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
 
 export const DefaultCellPackBaseUrl = 'https://mesoscope.scripps.edu/data/cellPACK_data/cellPACK_database_1.1.0/';
 
@@ -71,10 +73,10 @@ const StructureFromCellpack = PluginStateTransform.BuiltIn({
                     ingredientFiles[file.name] = file;
                 }
             }
-            const { structure, assets } = await createStructureFromCellPack(plugin, packing, params.baseUrl, ingredientFiles).runInContext(ctx);
-
+            const { structure, assets, colors } = await createStructureFromCellPack(plugin, packing, params.baseUrl, ingredientFiles).runInContext(ctx);
+            const docolor: boolean = (colors.length !== 0);
             await CellPackInfoProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, structure, {
-                info: { packingsCount: a.data.packings.length, packingIndex: params.packing }
+                info: { packingsCount: a.data.packings.length, packingIndex: params.packing, colors: colors, color: docolor }
             });
 
             (cache as any).assets = assets;
@@ -94,4 +96,113 @@ const StructureFromCellpack = PluginStateTransform.BuiltIn({
             }
         }
     }
-});
+});
+
+export { GetAllAssamblyinOne };
+type GetAllAssamblyinOne = typeof GetAllAssamblyinOne
+const GetAllAssamblyinOne = PluginStateTransform.BuiltIn({
+    name: 'get assambly from model',
+    display: { name: 'get assambly from model' },
+    isDecorator: true,
+    from: PSO.Molecule.Model,
+    to: PSO.Molecule.Structure,
+    params: {
+    }
+})({
+    canAutoUpdate({ newParams }) {
+        return true;
+    },
+    apply({ a, params }) {
+        return Task.create('Build Structure', async ctx => {
+            // TODO: optimze
+            // TODO: think of ways how to fast-track changes to this for animations
+            const model = a.data;
+            let initial_structure = Structure.ofModel(model);
+            const structures: Structure[] = [];
+            let structure: Structure = initial_structure;
+            // the list of asambly *?
+            const symmetry = ModelSymmetry.Provider.get(model);
+            if (symmetry){
+                if (symmetry.assemblies.length !== 0) {
+                    for (const a of symmetry.assemblies) {
+                        const s = await StructureSymmetry.buildAssembly(initial_structure, a.id).runInContext(ctx);
+                        structures.push(s);
+                    }
+                    const builder = Structure.Builder({ label: name });
+                    let offsetInvariantId = 0;
+                    for (const s of structures) {
+                        let maxInvariantId = 0;
+                        for (const u of s.units) {
+                            const invariantId = u.invariantId + offsetInvariantId;
+                            if (u.invariantId > maxInvariantId) maxInvariantId = u.invariantId;
+                            builder.addUnit(u.kind, u.model, u.conformation.operator, u.elements, Unit.Trait.None, invariantId);
+                        }
+                        offsetInvariantId += maxInvariantId + 1;
+                    }
+                    structure = builder.getStructure();
+                    for( let i = 0, il = structure.models.length; i < il; ++i) {
+                        const { trajectoryInfo } = structure.models[i];
+                        trajectoryInfo.size = il;
+                        trajectoryInfo.index = i;
+                    }
+                }
+            }
+            return new PSO.Molecule.Structure(structure, { label: a.label, description: `${a.description}` });
+        });
+    },
+    dispose({ b }) {
+        b?.data.customPropertyDescriptors.dispose();
+    }
+});
+
+export { GetAllAssamblyinOneStructure };
+type GetAllAssamblyinOneStructure = typeof GetAllAssamblyinOneStructure
+const GetAllAssamblyinOneStructure = PluginStateTransform.BuiltIn({
+    name: 'get assambly from structure',
+    display: { name: 'get assambly from structure' },
+    isDecorator: true,
+    from: PSO.Molecule.Structure,
+    to: PSO.Molecule.Structure,
+    params(a) {
+        return { };
+    }
+})({
+    apply({ a, params }) {
+        return Task.create('Build Structure Assemblies', async ctx => {
+            // TODO: optimze
+            // TODO: think of ways how to fast-track changes to this for animations
+            const initial_structure = a.data;
+            const structures: Structure[] = [];
+            let structure: Structure = initial_structure;
+            // the list of asambly *?
+            const symmetry = ModelSymmetry.Provider.get(initial_structure.model);
+            if (symmetry){
+                if (symmetry.assemblies.length !== 0) {
+                    for (const a of symmetry.assemblies) {
+                        const s = await StructureSymmetry.buildAssembly(initial_structure, a.id!).runInContext(ctx);
+                        structures.push(s);
+                    }
+                    const builder = Structure.Builder({ label: name });
+                    let offsetInvariantId = 0;
+                    for (const s of structures) {
+                        let maxInvariantId = 0;
+                        for (const u of s.units) {
+                            const invariantId = u.invariantId + offsetInvariantId;
+                            if (u.invariantId > maxInvariantId) maxInvariantId = u.invariantId;
+                            builder.addUnit(u.kind, u.model, u.conformation.operator, u.elements, Unit.Trait.None, invariantId);
+                        }
+                        offsetInvariantId += maxInvariantId + 1;
+                    }
+                    structure = builder.getStructure();
+                    for( let i = 0, il = structure.models.length; i < il; ++i) {
+                        const { trajectoryInfo } = structure.models[i];
+                        trajectoryInfo.size = il;
+                        trajectoryInfo.index = i;
+                    }
+                }
+            }
+            return new PSO.Molecule.Structure(structure, { label: a.label, description: `${a.description}` });
+        });
+    }
+});
+