Browse Source

Merge branch 'master' of https://github.com/molstar/molstar

Alexander Rose 4 years ago
parent
commit
ad1507dadf

File diff suppressed because it is too large
+ 664 - 152
package-lock.json


+ 17 - 17
package.json

@@ -87,33 +87,33 @@
   "license": "MIT",
   "devDependencies": {
     "@graphql-codegen/add": "^1.17.7",
-    "@graphql-codegen/cli": "^1.17.7",
+    "@graphql-codegen/cli": "^1.17.8",
     "@graphql-codegen/time": "^1.17.10",
-    "@graphql-codegen/typescript": "^1.17.7",
-    "@graphql-codegen/typescript-graphql-files-modules": "^1.17.7",
+    "@graphql-codegen/typescript": "^1.17.9",
+    "@graphql-codegen/typescript-graphql-files-modules": "^1.17.8",
     "@graphql-codegen/typescript-graphql-request": "^1.17.7",
-    "@graphql-codegen/typescript-operations": "^1.17.7",
+    "@graphql-codegen/typescript-operations": "^1.17.8",
     "@types/cors": "^2.8.7",
-    "@typescript-eslint/eslint-plugin": "^3.8.0",
-    "@typescript-eslint/parser": "^3.8.0",
+    "@typescript-eslint/eslint-plugin": "^3.9.1",
+    "@typescript-eslint/parser": "^3.9.1",
     "benchmark": "^2.1.4",
-    "concurrently": "^5.2.0",
+    "concurrently": "^5.3.0",
     "cpx2": "^2.0.0",
     "css-loader": "^3.6.0",
-    "eslint": "^7.6.0",
+    "eslint": "^7.7.0",
     "extra-watch-webpack-plugin": "^1.0.3",
     "file-loader": "^6.0.0",
     "fs-extra": "^9.0.1",
     "graphql": "^15.3.0",
     "http-server": "^0.12.3",
-    "jest": "^26.2.2",
+    "jest": "^26.4.1",
     "mini-css-extract-plugin": "^0.9.0",
     "node-sass": "^4.14.1",
     "raw-loader": "^4.0.1",
     "sass-loader": "^8.0.2",
-    "simple-git": "^2.17.0",
+    "simple-git": "^2.19.0",
     "style-loader": "^1.2.1",
-    "ts-jest": "^26.1.4",
+    "ts-jest": "^26.2.0",
     "typescript": "^3.9.7",
     "webpack": "^4.44.1",
     "webpack-cli": "^3.3.12",
@@ -125,9 +125,9 @@
     "@types/compression": "1.7.0",
     "@types/express": "^4.17.7",
     "@types/jest": "^25.2.3",
-    "@types/node": "^14.0.24",
+    "@types/node": "^14.6.0",
     "@types/node-fetch": "^2.5.7",
-    "@types/react": "^16.9.43",
+    "@types/react": "^16.9.46",
     "@types/react-dom": "^16.9.8",
     "@types/swagger-ui-dist": "3.0.5",
     "argparse": "^1.0.10",
@@ -135,14 +135,14 @@
     "compression": "^1.7.4",
     "cors": "^2.8.5",
     "express": "^4.17.1",
-    "immer": "^7.0.5",
+    "immer": "^7.0.7",
     "immutable": "^3.8.2",
     "node-fetch": "^2.6.0",
     "react": "^16.13.1",
     "react-dom": "^16.13.1",
-    "rxjs": "^6.6.0",
-    "swagger-ui-dist": "^3.30.1",
-    "tslib": "^2.0.0",
+    "rxjs": "^6.6.2",
+    "swagger-ui-dist": "^3.32.4",
+    "tslib": "^2.0.1",
     "util.promisify": "^1.0.1",
     "xhr2": "^0.2.0"
   }

+ 42 - 16
src/extensions/g3d/format.ts

@@ -5,15 +5,17 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { TrajectoryFormatProvider, TrajectoryFormatCategory } from '../../mol-plugin-state/formats/trajectory';
-import { PluginStateTransform, PluginStateObject as SO } from '../../mol-plugin-state/objects';
-import { G3dHeader, getG3dHeader, getG3dDataBlock } from './data';
-import { Task } from '../../mol-task';
+import { Trajectory } from '../../mol-model/structure';
+import { TrajectoryFormatCategory, TrajectoryFormatProvider } from '../../mol-plugin-state/formats/trajectory';
+import { PluginStateObject as SO, PluginStateTransform } from '../../mol-plugin-state/objects';
+import { PluginBehavior } from '../../mol-plugin/behavior';
 import { PluginContext } from '../../mol-plugin/context';
+import { DefaultQueryRuntimeTable } from '../../mol-script/runtime/query/base';
+import { StateAction, StateObjectRef } from '../../mol-state';
+import { Task } from '../../mol-task';
 import { ParamDefinition } from '../../mol-util/param-definition';
-import { trajectoryFromG3D } from './model';
-import { StateObjectRef, StateAction } from '../../mol-state';
-import { PluginBehavior } from '../../mol-plugin/behavior';
+import { G3dHeader, getG3dDataBlock, getG3dHeader } from './data';
+import { g3dHaplotypeQuery, G3dInfoPropertyProvider, G3dLabelProvider, trajectoryFromG3D } from './model';
 
 export const G3dProvider: TrajectoryFormatProvider = {
     label: 'G3D',
@@ -29,18 +31,29 @@ export const G3dProvider: TrajectoryFormatProvider = {
 
         return { trajectory };
     },
-    visuals: defaultVisuals
+    visuals: defaultStructure
 };
 
-async function defaultVisuals(plugin: PluginContext, data: { trajectory: StateObjectRef<SO.Molecule.Trajectory> }) {
+async function defaultStructure(plugin: PluginContext, data: { trajectory: StateObjectRef<SO.Molecule.Trajectory> }) {
     const builder = plugin.builders.structure;
     const model = await builder.createModel(data.trajectory);
     const modelProperties = await builder.insertModelProperties(model);
     const structure = await builder.createStructure(modelProperties);
-    const all = await builder.tryCreateComponentStatic(structure, 'all');
 
-    if (all) {
-        await builder.representation.addRepresentation(all, {
+    const m = await builder.tryCreateComponentFromExpression(structure, g3dHaplotypeQuery('maternal'), 'maternal', { label: 'Maternal' });
+    const p = await builder.tryCreateComponentFromExpression(structure, g3dHaplotypeQuery('paternal'), 'paternal', { label: 'Paternal' });
+
+    if (m) {
+        await builder.representation.addRepresentation(m, {
+            type: 'cartoon',
+            color: 'polymer-index',
+            size: 'uniform',
+            sizeParams: { value: 2 }
+        });
+    }
+
+    if (p) {
+        await builder.representation.addRepresentation(p, {
             type: 'cartoon',
             color: 'polymer-index',
             size: 'uniform',
@@ -51,7 +64,8 @@ async function defaultVisuals(plugin: PluginContext, data: { trajectory: StateOb
 
 export class G3dHeaderObject extends SO.Create<{
     header: G3dHeader,
-    urlOrData: Uint8Array | string
+    urlOrData: Uint8Array | string,
+    cache: { [resolution: number]: Trajectory | undefined }
 }>({ name: 'G3D Header', typeClass: 'Data' }) { }
 
 export type G3DHeaderFromFile = typeof G3DHeaderFromFile
@@ -64,7 +78,7 @@ export const G3DHeaderFromFile = PluginStateTransform.BuiltIn({
     apply({ a }, plugin: PluginContext) {
         return Task.create('Parse G3D', async () => {
             const header = await getG3dHeader(plugin, a.data);
-            return new G3dHeaderObject({ header, urlOrData: a.data }, { label: header.name, description: header.genome });
+            return new G3dHeaderObject({ header, urlOrData: a.data, cache: { } }, { label: header.name, description: header.genome });
         });
     }
 });
@@ -80,7 +94,7 @@ export const G3DHeaderFromUrl = PluginStateTransform.BuiltIn({
     apply({ params }, plugin: PluginContext) {
         return Task.create('Parse G3D', async () => {
             const header = await getG3dHeader(plugin, params.url);
-            return new G3dHeaderObject({ header, urlOrData: params.url }, { label: header.name, description: header.genome });
+            return new G3dHeaderObject({ header, urlOrData: params.url, cache: { } }, { label: header.name, description: header.genome });
         });
     }
 });
@@ -101,8 +115,12 @@ export const G3DTrajectory = PluginStateTransform.BuiltIn({
 })({
     apply({ a, params }, plugin: PluginContext) {
         return Task.create('G3D Trajectory', async ctx => {
+            if (a.data.cache[params.resolution]) {
+                return new SO.Molecule.Trajectory(a.data.cache[params.resolution]!, { label: a.label, description: a.description });
+            }
             const data = await getG3dDataBlock(plugin, a.data.header, a.data.urlOrData, params.resolution);
             const traj = await trajectoryFromG3D(data).runInContext(ctx);
+            a.data.cache[params.resolution] = traj;
             return new SO.Molecule.Trajectory(traj, { label: a.label, description: a.description });
         });
     }
@@ -125,7 +143,7 @@ export const LoadG3D = StateAction.build({
             .apply(G3DTrajectory)
             .commit();
 
-        await defaultVisuals(ctx, { trajectory });
+        await defaultStructure(ctx, { trajectory });
     }).runInContext(taskCtx);
 }));
 
@@ -139,9 +157,17 @@ export const G3DFormat = PluginBehavior.create<{ autoAttach: boolean, showToolti
     ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showTooltip: boolean }> {
         register() {
             this.ctx.state.data.actions.add(LoadG3D);
+
+            DefaultQueryRuntimeTable.addCustomProp(G3dInfoPropertyProvider.descriptor);
+            this.ctx.customModelProperties.register(G3dInfoPropertyProvider, false);
+            this.ctx.managers.lociLabels.addProvider(G3dLabelProvider);
         }
         unregister() {
             this.ctx.state.data.actions.remove(LoadG3D);
+
+            DefaultQueryRuntimeTable.removeCustomProp(G3dInfoPropertyProvider.descriptor);
+            this.ctx.customModelProperties.unregister(G3dInfoPropertyProvider.descriptor.name);
+            this.ctx.managers.lociLabels.removeProvider(G3dLabelProvider);
         }
     }
 });

+ 115 - 39
src/extensions/g3d/model.ts

@@ -4,24 +4,34 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { Task } from '../../mol-task';
 import { Column, Table } from '../../mol-data/db';
-import { MoleculeType } from '../../mol-model/structure/model/types';
-import { EntityBuilder } from '../../mol-model-formats/structure/common/entity';
-import { BasicSchema, createBasic } from '../../mol-model-formats/structure/basic/schema';
 import { createModels } from '../../mol-model-formats/structure/basic/parser';
-import { G3dDataBlock } from './data';
+import { BasicSchema, createBasic } from '../../mol-model-formats/structure/basic/schema';
+import { EntityBuilder } from '../../mol-model-formats/structure/common/entity';
+import { CustomModelProperty } from '../../mol-model-props/common/custom-model-property';
+import { Model, Trajectory, Unit } from '../../mol-model/structure';
+import { MoleculeType } from '../../mol-model/structure/model/types';
+import { CustomPropSymbol } from '../../mol-script/language/symbol';
+import Type from '../../mol-script/language/type';
+import { QuerySymbolRuntime } from '../../mol-script/runtime/query/base';
+import { Task, RuntimeContext } from '../../mol-task';
 import { objectForEach } from '../../mol-util/object';
-import { Trajectory } from '../../mol-model/structure';
+import { ParamDefinition } from '../../mol-util/param-definition';
+import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
+import { G3dDataBlock } from './data';
+import { Loci } from '../../mol-model/loci';
+import { LociLabelProvider } from '../../mol-plugin-state/manager/loci-label';
+import { OrderedSet } from '../../mol-data/int';
 
-interface Columns {
+interface NormalizedData {
     entity_id: string[],
     chromosome: string[],
+    seq_id_begin: Int32Array,
+    seq_id_end: Int32Array,
     start: Int32Array,
-    end: Int32Array,
-    x: Float32Array
-    y: Float32Array
-    z: Float32Array
+    x: Float32Array,
+    y: Float32Array,
+    z: Float32Array,
     haplotype: string[]
 }
 
@@ -31,11 +41,12 @@ function getColumns(block: G3dDataBlock) {
 
     objectForEach(data, h => objectForEach(h, g => size += g.start.length));
 
-    const columns: Columns = {
+    const normalized: NormalizedData = {
         entity_id: new Array(size),
         chromosome: new Array(size),
+        seq_id_begin: new Int32Array(size),
+        seq_id_end: new Int32Array(size),
         start: new Int32Array(size),
-        end: new Int32Array(size),
         x: new Float32Array(size),
         y: new Float32Array(size),
         z: new Float32Array(size),
@@ -47,56 +58,58 @@ function getColumns(block: G3dDataBlock) {
         objectForEach(hs, (chs, ch) => {
             const entity_id = `${ch}-${h}`;
             for (let i = 0, _i = chs.start.length; i < _i; i++) {
-                columns.entity_id[o] = entity_id;
-                columns.chromosome[o] = ch;
-                columns.start[o] = o + 1;
-                columns.end[o] = o + 2;
-                columns.x[o] = 10 * chs.x[i];
-                columns.y[o] = 10 * chs.y[i];
-                columns.z[o] = 10 * chs.z[i];
-                columns.haplotype[o] = h;
+                normalized.entity_id[o] = entity_id;
+                normalized.chromosome[o] = ch;
+                normalized.start[o] = chs.start[i];
+                normalized.seq_id_begin[o] = o;
+                normalized.seq_id_end[o] = o;
+                normalized.x[o] = 10 * chs.x[i];
+                normalized.y[o] = 10 * chs.y[i];
+                normalized.z[o] = 10 * chs.z[i];
+                normalized.haplotype[o] = h;
                 o++;
             }
         });
     });
 
-    return columns;
+    return normalized;
 }
 
-function getBasic(data: G3dDataBlock) {
-    const columns = getColumns(data);
+async function getTraj(ctx: RuntimeContext, data: G3dDataBlock) {
+    const normalized = getColumns(data);
 
-    const rowCount = columns.start.length;
+    const rowCount = normalized.seq_id_begin.length;
     const entityIds = new Array<string>(rowCount);
     const entityBuilder = new EntityBuilder();
 
-    const stride = columns.start[1] - columns.start[0];
-
+    const stride = normalized.seq_id_begin[1] - normalized.seq_id_begin[0];
     const objectRadius = stride / 3500;
 
+    const eName = { customName: '' };
     for (let i = 0; i < rowCount; ++i) {
-        const e = columns.entity_id[i];
-        const entityId = entityBuilder.getEntityId(e, MoleculeType.DNA, e);
+        const e = normalized.entity_id[i];
+        eName.customName = e;
+        const entityId = entityBuilder.getEntityId(e, MoleculeType.DNA, e, eName);
         entityIds[i] = entityId;
     }
 
     const ihm_sphere_obj_site = Table.ofPartialColumns(BasicSchema.ihm_sphere_obj_site, {
         id: Column.range(0, rowCount),
         entity_id: Column.ofStringArray(entityIds),
-        seq_id_begin: Column.ofIntArray(columns.start),
-        seq_id_end: Column.ofIntArray(columns.end),
-        asym_id: Column.ofStringArray(columns.chromosome),
+        seq_id_begin: Column.ofIntArray(normalized.seq_id_begin),
+        seq_id_end: Column.ofIntArray(normalized.seq_id_end),
+        asym_id: Column.ofStringArray(normalized.chromosome),
 
-        Cartn_x: Column.ofFloatArray(columns.x),
-        Cartn_y: Column.ofFloatArray(columns.y),
-        Cartn_z: Column.ofFloatArray(columns.z),
+        Cartn_x: Column.ofFloatArray(normalized.x),
+        Cartn_y: Column.ofFloatArray(normalized.y),
+        Cartn_z: Column.ofFloatArray(normalized.z),
 
         object_radius: Column.ofConst(objectRadius, rowCount, Column.Schema.float),
         rmsf: Column.ofConst(0, rowCount, Column.Schema.float),
         model_id: Column.ofConst(1, rowCount, Column.Schema.int),
     }, rowCount);
 
-    return createBasic({
+    const basic = createBasic({
         entity: entityBuilder.getEntityTable(),
         ihm_model_list: Table.ofPartialColumns(BasicSchema.ihm_model_list, {
             model_id: Column.ofIntArray([1]),
@@ -104,11 +117,74 @@ function getBasic(data: G3dDataBlock) {
         }, 1),
         ihm_sphere_obj_site
     });
+
+    const models = await createModels(basic, { kind: 'g3d', name: 'G3D', data }, ctx);
+
+    await G3dInfoPropertyProvider.attach({ runtime: ctx, assetManager: void 0 as any }, models.representative, {
+        resolution: data.resolution,
+        haplotype: normalized.haplotype,
+        start: normalized.start
+    });
+
+    return models;
 }
 
 export function trajectoryFromG3D(data: G3dDataBlock): Task<Trajectory> {
     return Task.create('Parse G3D', async ctx => {
-        const basic = getBasic(data);
-        return createModels(basic, { kind: 'g3d', name: 'G3D', data }, ctx);
+        return getTraj(ctx, data);
+    });
+}
+
+export const G3dSymbols = {
+    haplotype: QuerySymbolRuntime.Dynamic(CustomPropSymbol('g3d', 'haplotype', Type.Str),
+        ctx => {
+            if (Unit.isAtomic(ctx.element.unit)) return '';
+            const info = G3dInfoPropertyProvider.get(ctx.element.unit.model);
+            const seqId = ctx.element.unit.model.coarseHierarchy.spheres.seq_id_begin.value(ctx.element.element);
+            return info.value?.haplotype[seqId] || '';
+        }
+    )
+};
+
+export function g3dHaplotypeQuery(haplotype: 'maternal' | 'paternal') {
+    return MS.struct.generator.atomGroups({
+        'chain-test': MS.core.rel.eq([G3dSymbols.haplotype.symbol(), haplotype]),
     });
-}
+}
+
+export const G3dInfoPropertyParams = {
+    haplotype: ParamDefinition.Value<string[]>([]),
+    start: ParamDefinition.Value<Int32Array>(new Int32Array(0)),
+    resolution: ParamDefinition.Numeric(0)
+};
+
+export type G3dInfoPropertyParams = typeof G3dInfoPropertyParams
+export type G3dInfoPropertyParamsProps = ParamDefinition.Values<G3dInfoPropertyParams>
+
+export const G3dInfoPropertyProvider: CustomModelProperty.Provider<G3dInfoPropertyParams, G3dInfoPropertyParamsProps> = CustomModelProperty.createProvider({
+    label: 'G3d Info',
+    type: 'static',
+    isHidden: true,
+    defaultParams: G3dInfoPropertyParams,
+    descriptor: { name: 'g3d_info', symbols: G3dSymbols },
+    getParams: (data: Model) => G3dInfoPropertyParams,
+    isApplicable: (data: Model) => true,
+    obtain: async (ctx, data, props) => {
+        return { value: props as any };
+    }
+});
+
+export const G3dLabelProvider: LociLabelProvider = {
+    label: (e: Loci): string | undefined => {
+        if (e.kind !== 'element-loci' || Loci.isEmpty(e)) return;
+
+        const first = e.elements[0];
+        if (e.elements.length !== 1 || Unit.isAtomic(first.unit)) return;
+        const info = G3dInfoPropertyProvider.get(first.unit.model);
+        if (!info) return;
+
+        const eI = first.unit.elements[OrderedSet.getAt(first.indices, 0)];
+        const seqId = first.unit.model.coarseHierarchy.spheres.seq_id_begin.value(eI);
+        return `<b>Start:</b> ${info.value?.start[seqId]} <small>| resolution ${info.value?.resolution}<small>`;
+    }
+};

+ 4 - 4
src/mol-model-formats/structure/common/entity.ts

@@ -32,10 +32,10 @@ export class EntityBuilder {
         this.descriptions.push([description]);
     }
 
-    getEntityId(compId: string, moleculeType: MoleculeType, chainId: string): string {
+    getEntityId(compId: string, moleculeType: MoleculeType, chainId: string, options?: { customName?: string }): string {
         if (moleculeType === MoleculeType.Water) {
             if (this.waterId === undefined) {
-                this.set('water', 'Water');
+                this.set('water', options?.customName || 'Water');
                 this.waterId = `${this.count}`;
             }
             return this.waterId;
@@ -44,14 +44,14 @@ export class EntityBuilder {
                 return this.compoundsMap.get(chainId)!;
             } else {
                 if (!this.chainMap.has(chainId)) {
-                    this.set('polymer', `Polymer ${this.chainMap.size + 1}`);
+                    this.set('polymer', options?.customName || `Polymer ${this.chainMap.size + 1}`);
                     this.chainMap.set(chainId, `${this.count}`);
                 }
                 return this.chainMap.get(chainId)!;
             }
         } else {
             if (!this.heteroMap.has(compId)) {
-                this.set('non-polymer', this.namesMap.get(compId) || compId);
+                this.set('non-polymer', options?.customName || this.namesMap.get(compId) || compId);
                 this.heteroMap.set(compId, `${this.count}`);
             }
             return this.heteroMap.get(compId)!;

+ 1 - 1
src/mol-theme/label.ts

@@ -294,7 +294,7 @@ function _coarseElementLabel(location: StructureElement.Location<Unit.Spheres |
                 const entityIndex = Props.coarse.entityKey(location);
                 const seq = location.unit.model.sequence.byEntityKey[entityIndex];
                 const comp_id = seq.sequence.compId.value(seq_id_begin - 1); // 1-indexed
-                label.push(`<b>${comp_id} ${seq_id_begin}-${seq_id_end}</b>`);
+                label.push(`<b>${comp_id} ${seq_id_begin}</b>`);
             } else {
                 label.push(`<b>${seq_id_begin}-${seq_id_end}</b>`);
             }

Some files were not shown because too many files changed in this diff