Bladeren bron

wip, dsn6/brix support

Alexander Rose 6 jaren geleden
bovenliggende
commit
6cbef342ce

+ 1 - 1
src/mol-io/reader/ccp4/parser.ts

@@ -10,7 +10,7 @@ import Result from '../result'
 import { FileHandle } from '../../common/file-handle';
 
 async function parseInternal(file: FileHandle, ctx: RuntimeContext): Promise<Result<Ccp4File>> {
-    await ctx.update({ message: 'Parsing CCP4 file...' });
+    await ctx.update({ message: 'Parsing CCP4/MRC file...' });
 
     const { buffer } = await file.readBuffer(0, file.length)
     const bin = buffer.buffer

+ 10 - 9
src/mol-io/reader/dsn6/parser.ts

@@ -26,9 +26,9 @@ function parseBrixHeader(str: string): Dsn6Header {
         alpha: parseFloat(str.substr(103, 10)),
         beta: parseFloat(str.substr(113, 10)),
         gamma: parseFloat(str.substr(123, 10)),
-        divisor: parseFloat(str.substr(138, 12)) / 100,
+        divisor: parseFloat(str.substr(138, 12)),
         summand: parseInt(str.substr(155, 8)),
-        sigma: parseFloat(str.substr(170, 12)) * 100
+        sigma: parseFloat(str.substr(170, 12))
     }
 }
 
@@ -57,7 +57,7 @@ function parseDsn6Header(int: Int16Array): Dsn6Header {
 }
 
 async function parseInternal(file: FileHandle, ctx: RuntimeContext): Promise<Result<Dsn6File>> {
-    await ctx.update({ message: 'Parsing CCP4 file...' });
+    await ctx.update({ message: 'Parsing DSN6/BRIX file...' });
 
     const { buffer } = await file.readBuffer(0, file.length)
     const bin = buffer.buffer
@@ -67,18 +67,19 @@ async function parseInternal(file: FileHandle, ctx: RuntimeContext): Promise<Res
     const brixStr = String.fromCharCode.apply(null, byteView.subarray(0, 512))
     const isBrix = brixStr.startsWith(':-)')
 
-    const header = isBrix ? parseBrixHeader(brixStr) : parseDsn6Header(intView)
-    const { divisor, summand } = header
-
     if (!isBrix) {
         // for DSN6, swap byte order when big endian
-        if (intView[ 18 ] !== 100) {
+        if (intView[18] !== 100) {
             for (let i = 0, n = intView.length; i < n; ++i) {
-              const val = intView[ i ]
-                intView[ i ] = ((val & 0xff) << 8) | ((val >> 8) & 0xff)
+                const val = intView[i]
+                intView[i] = ((val & 0xff) << 8) | ((val >> 8) & 0xff)
             }
         }
     }
+
+    const header = isBrix ? parseBrixHeader(brixStr) : parseDsn6Header(intView)
+    const { divisor, summand } = header
+
     const values = new Float32Array(header.xExtent * header.yExtent * header.zExtent)
 
     let offset = 512

+ 1 - 1
src/mol-model/volume/formats/dsn6.ts

@@ -28,7 +28,7 @@ function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3 }): Task<V
         const origin_frac = Vec3.create(gridOrigin[0] / grid[0], gridOrigin[1] / grid[1], gridOrigin[2] / grid[2]);
         const dimensions_frac = Vec3.create(extent[0] / grid[0], extent[1] / grid[1], extent[2] / grid[2]);
 
-        const space = Tensor.Space(extent, [2, 1, 0], Float32Array);
+        const space = Tensor.Space(extent, [0, 1, 2], Float32Array);
         const data = Tensor.create(space, Tensor.Data1(values));
 
         return {

+ 35 - 7
src/mol-plugin/state/actions/basic.ts

@@ -15,6 +15,7 @@ import { PluginStateObject } from '../objects';
 import { StateTransforms } from '../transforms';
 import { Download } from '../transforms/data';
 import { StructureRepresentation3DHelpers, VolumeRepresentation3DHelpers } from '../transforms/representation';
+import { getFileInfo } from 'mol-util/file-info';
 
 // TODO: "structure parser provider"
 
@@ -161,25 +162,52 @@ export const UpdateTrajectory = StateAction.build({
 
 //
 
-function createVolumeTree(ctx: PluginContext, b: StateTreeBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>): StateTree {
-    let root = b
-        .apply(StateTransforms.Data.ParseCcp4)
-        .apply(StateTransforms.Model.VolumeFromCcp4)
+const VolumeFormats = { 'ccp4': '', 'mrc': '', 'dsn6': '', 'brix': '' }
+type VolumeFormat = keyof typeof VolumeFormats
+
+function getVolumeData(format: VolumeFormat, b: StateTreeBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>) {
+    switch (format) {
+        case 'ccp4': case 'mrc':
+            return b.apply(StateTransforms.Data.ParseCcp4).apply(StateTransforms.Model.VolumeFromCcp4);
+        case 'dsn6': case 'brix':
+            return b.apply(StateTransforms.Data.ParseDsn6).apply(StateTransforms.Model.VolumeFromDsn6);
+    }
+}
+
+function createVolumeTree(format: VolumeFormat, ctx: PluginContext, b: StateTreeBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>): StateTree {
+
+    const root = getVolumeData(format, b)
         .apply(StateTransforms.Representation.VolumeRepresentation3D,
             VolumeRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'isosurface'));
 
     return root.getTree();
 }
 
+function getFileFormat(format: VolumeFormat | 'auto', file: File): VolumeFormat {
+    if (format === 'auto') {
+        const fileFormat = getFileInfo(file).ext
+        if (fileFormat in VolumeFormats) {
+            return fileFormat as VolumeFormat
+        } else {
+            throw new Error('unsupported format')
+        }
+    } else {
+        return format
+    }
+}
+
 export const OpenVolume = StateAction.build({
     display: { name: 'Open Volume', description: 'Load a volume from file and create its default visual' },
     from: PluginStateObject.Root,
     params: {
-        file: PD.File({ accept: '.ccp4,.mrc'}),
-        // format: PD.Select('auto', [['auto', 'Automatic'], ['ccp4', 'CCP4'], ['mrc', 'MRC']]),
+        file: PD.File({ accept: '.ccp4,.mrc,.dsn6,.brix'}),
+        format: PD.Select('auto', [
+            ['auto', 'Automatic'], ['ccp4', 'CCP4'], ['mrc', 'MRC'], ['dsn6', 'DSN6'], ['brix', 'BRIX']
+        ]),
     }
 })(({ params, state }, ctx: PluginContext) => {
     const b = state.build();
     const data = b.toRoot().apply(StateTransforms.Data.ReadFile, { file: params.file, isBinary: true });
-    return state.update(createVolumeTree(ctx, data));
+    const format = getFileFormat(params.format, params.file)
+    return state.update(createVolumeTree(format, ctx, data));
 });

+ 3 - 1
src/mol-plugin/state/objects.ts

@@ -14,6 +14,7 @@ import { StructureRepresentation } from 'mol-repr/structure/representation';
 import { VolumeRepresentation } from 'mol-repr/volume/representation';
 import { StateObject, Transformer } from 'mol-state';
 import { Ccp4File } from 'mol-io/reader/ccp4/schema';
+import { Dsn6File } from 'mol-io/reader/dsn6/schema';
 
 export type TypeClass = 'root' | 'data' | 'prop'
 
@@ -58,7 +59,8 @@ export namespace PluginStateObject {
     export namespace Format {
         export class Json extends Create<any>({ name: 'JSON Data', typeClass: 'Data' }) { }
         export class Cif extends Create<CifFile>({ name: 'CIF File', typeClass: 'Data' }) { }
-        export class Ccp4 extends Create<Ccp4File>({ name: 'CCP4 File', typeClass: 'Data' }) { }
+        export class Ccp4 extends Create<Ccp4File>({ name: 'CCP4/MRC File', typeClass: 'Data' }) { }
+        export class Dsn6 extends Create<Dsn6File>({ name: 'DSN6/BRIX File', typeClass: 'Data' }) { }
     }
 
     export namespace Molecule {

+ 20 - 2
src/mol-plugin/state/transforms/data.ts

@@ -14,6 +14,7 @@ import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Transformer } from 'mol-state';
 import { readFromFile } from 'mol-util/data-source';
 import * as CCP4 from 'mol-io/reader/ccp4/parser'
+import * as DSN6 from 'mol-io/reader/dsn6/parser'
 
 export { Download }
 type Download = typeof Download
@@ -98,15 +99,32 @@ export { ParseCcp4 }
 type ParseCcp4 = typeof ParseCcp4
 const ParseCcp4 = PluginStateTransform.BuiltIn({
     name: 'parse-ccp4',
-    display: { name: 'Parse CCP4', description: 'Parse CCP4 from Binary data' },
+    display: { name: 'Parse CCP4/MRC', description: 'Parse CCP4/MRC from Binary data' },
     from: [SO.Data.Binary],
     to: SO.Format.Ccp4
 })({
     apply({ a }) {
-        return Task.create('Parse CCP4', async ctx => {
+        return Task.create('Parse CCP4/MRC', async ctx => {
             const parsed = await CCP4.parse(a.data).runInContext(ctx);
             if (parsed.isError) throw new Error(parsed.message);
             return new SO.Format.Ccp4(parsed.result);
         });
     }
+});
+
+export { ParseDsn6 }
+type ParseDsn6 = typeof ParseDsn6
+const ParseDsn6 = PluginStateTransform.BuiltIn({
+    name: 'parse-dsn6',
+    display: { name: 'Parse DSN6/BRIX', description: 'Parse CCP4/BRIX from Binary data' },
+    from: [SO.Data.Binary],
+    to: SO.Format.Dsn6
+})({
+    apply({ a }) {
+        return Task.create('Parse DSN6/BRIX', async ctx => {
+            const parsed = await DSN6.parse(a.data).runInContext(ctx);
+            if (parsed.isError) throw new Error(parsed.message);
+            return new SO.Format.Dsn6(parsed.result);
+        });
+    }
 });

+ 23 - 0
src/mol-plugin/state/transforms/model.ts

@@ -18,6 +18,7 @@ import { PluginContext } from 'mol-plugin/context';
 import { stringToWords } from 'mol-util/string';
 import { volumeFromCcp4 } from 'mol-model/volume/formats/ccp4';
 import { Vec3 } from 'mol-math/linear-algebra';
+import { volumeFromDsn6 } from 'mol-model/volume/formats/dsn6';
 
 export { TrajectoryFromMmCif }
 type TrajectoryFromMmCif = typeof TrajectoryFromMmCif
@@ -233,4 +234,26 @@ const VolumeFromCcp4 = PluginStateTransform.BuiltIn({
             return new SO.Volume.Data(volume, props);
         });
     }
+});
+
+export { VolumeFromDsn6 }
+type VolumeFromDsn6 = typeof VolumeFromDsn6
+const VolumeFromDsn6 = PluginStateTransform.BuiltIn({
+    name: 'volume-from-dsn6',
+    display: { name: 'Volume from DSN6/BRIX', description: 'Create Volume from DSN6/BRIX data' },
+    from: SO.Format.Dsn6,
+    to: SO.Volume.Data,
+    params(a) {
+        return {
+            voxelSize: PD.Vec3(Vec3.create(1, 1, 1))
+        };
+    }
+})({
+    apply({ a, params }) {
+        return Task.create('Create volume from DSN6/BRIX', async ctx => {
+            const volume = await volumeFromDsn6(a.data, params).runInContext(ctx)
+            const props = { label: 'Volume' };
+            return new SO.Volume.Data(volume, props);
+        });
+    }
 });