Ver Fonte

volume label improvements

- add cell value to loci label
- add file name to volume data objects
Alexander Rose há 5 anos atrás
pai
commit
e2857d00b4

+ 5 - 2
src/mol-io/common/file-handle.ts

@@ -11,6 +11,7 @@ import { SimpleBuffer } from './simple-buffer';
 const fs = typeof document === 'undefined' ? require('fs') as typeof import('fs') : void 0;
 
 export interface FileHandle {
+    name: string
     /**
      * Asynchronously reads data, returning buffer and number of bytes read
      *
@@ -44,8 +45,9 @@ export interface FileHandle {
 }
 
 export namespace FileHandle {
-    export function fromBuffer(buffer: SimpleBuffer): FileHandle {
+    export function fromBuffer(buffer: SimpleBuffer, name: string): FileHandle {
         return {
+            name,
             readBuffer: (position: number, sizeOrBuffer: SimpleBuffer | number, size?: number, byteOffset?: number) => {
                 let bytesRead: number;
                 let outBuffer: SimpleBuffer;
@@ -82,9 +84,10 @@ export namespace FileHandle {
         };
     }
 
-    export function fromDescriptor(file: number): FileHandle {
+    export function fromDescriptor(file: number, name: string): FileHandle {
         if (fs === undefined) throw new Error('fs module not available');
         return {
+            name,
             readBuffer: (position: number, sizeOrBuffer: SimpleBuffer | number, length?: number, byteOffset?: number) => {
                 return new Promise((res, rej) => {
                     let outBuffer: SimpleBuffer;

+ 1 - 1
src/mol-io/reader/_spec/ccp4.spec.ts

@@ -31,7 +31,7 @@ function createCcp4Data() {
 describe('ccp4 reader', () => {
     it('basic', async () => {
         const data = createCcp4Data();
-        const parsed = await CCP4.parse(data).run();
+        const parsed = await CCP4.parse(data, 'test.ccp4').run();
 
         if (parsed.isError) {
             throw new Error(parsed.message);

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

@@ -160,7 +160,7 @@ async function parseInternal(file: FileHandle, size: number, ctx: RuntimeContext
     const buffer = createTypedArrayBufferContext(count, valueType);
     readCcp4Slices(header, buffer, file, offset, byteCount, littleEndian);
 
-    const result: Ccp4File = { header, values: buffer.values };
+    const result: Ccp4File = { header, values: buffer.values, name: file.name };
     return result;
 }
 
@@ -174,6 +174,6 @@ export function parseFile(file: FileHandle, size: number) {
     });
 }
 
-export function parse(buffer: Uint8Array) {
-    return parseFile(FileHandle.fromBuffer(SimpleBuffer.fromUint8Array(buffer)), buffer.length);
+export function parse(buffer: Uint8Array, name: string) {
+    return parseFile(FileHandle.fromBuffer(SimpleBuffer.fromUint8Array(buffer), name), buffer.length);
 }

+ 1 - 0
src/mol-io/reader/ccp4/schema.ts

@@ -114,6 +114,7 @@ export interface Ccp4Header {
  * CCP4 format does not use the ORIGIN header records (words 50-52)
  */
 export interface Ccp4File {
+    name: string
     header: Ccp4Header
     values: Float32Array | Int16Array | Int8Array | Uint16Array
 }

+ 3 - 2
src/mol-io/reader/cube/parser.ts

@@ -17,6 +17,7 @@ import { parseFloat as fastParseFloat } from '../common/text/number-parser';
 // https://h5cube-spec.readthedocs.io/en/latest/cubeformat.html
 
 export interface CubeFile {
+    name: string,
     header: CubeFile.Header,
     atoms: CubeFile.Atoms,
     values: Float64Array
@@ -129,12 +130,12 @@ function readValues(ctx: RuntimeContext, tokenizer: Tokenizer, header: CubeFile.
     }, (ctx, _, i) => ctx.update({ current: Math.min(i, N), max: N }));
 }
 
-export function parseCube(data: string) {
+export function parseCube(data: string, name: string) {
     return Task.create<Result<CubeFile>>('Parse Cube', async taskCtx => {
         await taskCtx.update('Reading header...');
         const tokenizer = Tokenizer(data);
         const { header, atoms } = readHeader(tokenizer);
         const values = await readValues(taskCtx, tokenizer, header);
-        return Result.success({ header, atoms, values });
+        return Result.success({ header, atoms, values, name });
     });
 }

+ 3 - 3
src/mol-io/reader/dsn6/parser.ts

@@ -134,7 +134,7 @@ async function parseInternal(file: FileHandle, size: number, ctx: RuntimeContext
     const values = new Float32Array(valueCount);
     await parseDsn6Values(header, buffer, values, littleEndian);
 
-    const result: Dsn6File = { header, values };
+    const result: Dsn6File = { header, values, name: file.name };
     return result;
 }
 
@@ -148,6 +148,6 @@ export function parseFile(file: FileHandle, size: number) {
     });
 }
 
-export function parse(buffer: Uint8Array) {
-    return parseFile(FileHandle.fromBuffer(SimpleBuffer.fromUint8Array(buffer)), buffer.length);
+export function parse(buffer: Uint8Array, name: string) {
+    return parseFile(FileHandle.fromBuffer(SimpleBuffer.fromUint8Array(buffer), name), buffer.length);
 }

+ 1 - 0
src/mol-io/reader/dsn6/schema.ts

@@ -39,6 +39,7 @@ export interface Dsn6Header {
  * BRIX http://svn.cgl.ucsf.edu/svn/chimera/trunk/libs/VolumeData/dsn6/brix-1.html
  */
 export interface Dsn6File {
+    name: string
     header: Dsn6Header
     values: Float32Array
 }

+ 8 - 7
src/mol-io/reader/dx/parser.ts

@@ -17,6 +17,7 @@ import { utf8Read } from '../../common/utf8';
 // http://apbs-pdb2pqr.readthedocs.io/en/latest/formats/opendx.html
 
 export interface DxFile {
+    name: string,
     header: DxFile.Header,
     values: Float64Array
 }
@@ -88,16 +89,16 @@ function readValuesText(ctx: RuntimeContext, tokenizer: Tokenizer, header: DxFil
     }, (ctx, _, i) => ctx.update({ current: Math.min(i, N), max: N }));
 }
 
-async function parseText(taskCtx: RuntimeContext, data: string) {
+async function parseText(taskCtx: RuntimeContext, data: string, name: string) {
     await taskCtx.update('Reading header...');
     const tokenizer = Tokenizer(data as string);
     const { header } = readHeader(tokenizer);
     await taskCtx.update('Reading values...');
     const values = await readValuesText(taskCtx, tokenizer, header);
-    return Result.success({ header, values });
+    return Result.success({ header, values, name });
 }
 
-async function parseBinary(taskCtx: RuntimeContext, data: Uint8Array) {
+async function parseBinary(taskCtx: RuntimeContext, data: Uint8Array, name: string) {
     await taskCtx.update('Reading header...');
 
     const headerString = utf8Read(data, 0, 1000);
@@ -117,12 +118,12 @@ async function parseBinary(taskCtx: RuntimeContext, data: Uint8Array) {
 
     // TODO: why doesnt this work? throw "attempting to construct out-of-bounds TypedArray"
     // const values = new Float64Array(data.buffer, data.byteOffset + headerByteCount, header.dim[0] * header.dim[1] * header.dim[2]);
-    return Result.success({ header, values });
+    return Result.success({ header, values, name });
 }
 
-export function parseDx(data: string | Uint8Array) {
+export function parseDx(data: string | Uint8Array, name: string) {
     return Task.create<Result<DxFile>>('Parse Cube', taskCtx => {
-        if (typeof data === 'string') return parseText(taskCtx, data);
-        return parseBinary(taskCtx, data);
+        if (typeof data === 'string') return parseText(taskCtx, data, name);
+        return parseBinary(taskCtx, data, name);
     });
 }

+ 4 - 2
src/mol-plugin-state/formats/volume.ts

@@ -68,9 +68,11 @@ export const DxProvider = DataFormatProvider({
     stringExtensions: ['dx'],
     binaryExtensions: ['dxbin'],
     parse: async (plugin, data) => {
-        const volume = plugin.build()
+        const format = plugin.build()
             .to(data)
-            .apply(StateTransforms.Volume.VolumeFromDx);
+            .apply(StateTransforms.Data.ParseDx, {}, { state: { isGhost: true } });
+
+        const volume = format.apply(StateTransforms.Volume.VolumeFromDx);
 
         await volume.commit({ revertOnError: true });
 

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

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -22,6 +22,7 @@ import { StructureRepresentation, StructureRepresentationState } from '../mol-re
 import { VolumeRepresentation } from '../mol-repr/volume/representation';
 import { StateObject, StateTransformer } from '../mol-state';
 import { CubeFile } from '../mol-io/reader/cube/parser';
+import { DxFile } from '../mol-io/reader/dx/parser';
 
 export type TypeClass = 'root' | 'data' | 'prop'
 
@@ -73,6 +74,7 @@ export namespace PluginStateObject {
         export class Ply extends Create<PlyFile>({ name: 'PLY File', typeClass: 'Data' }) { }
         export class Ccp4 extends Create<Ccp4File>({ name: 'CCP4/MRC/MAP File', typeClass: 'Data' }) { }
         export class Dsn6 extends Create<Dsn6File>({ name: 'DSN6/BRIX File', typeClass: 'Data' }) { }
+        export class Dx extends Create<DxFile>({ name: 'DX File', typeClass: 'Data' }) { }
 
         export type BlobEntry = { id: string } & (
             { kind: 'json', data: unknown } |
@@ -85,6 +87,7 @@ export namespace PluginStateObject {
             { kind: 'dcd', data: DcdFile } |
             { kind: 'ccp4', data: Ccp4File } |
             { kind: 'dsn6', data: Dsn6File } |
+            { kind: 'dx', data: DxFile } |
             { kind: 'ply', data: PlyFile } |
             // For non-build in extensions
             { kind: 'custom', data: unknown, tag: string }

+ 21 - 3
src/mol-plugin-state/transforms/data.ts

@@ -19,6 +19,7 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { PluginStateObject as SO, PluginStateTransform } from '../objects';
 import { Asset } from '../../mol-util/assets';
 import { parseCube } from '../../mol-io/reader/cube/parser';
+import { parseDx } from '../../mol-io/reader/dx/parser';
 
 export { Download };
 export { DownloadBlob };
@@ -31,6 +32,7 @@ export { ParsePsf };
 export { ParsePly };
 export { ParseCcp4 };
 export { ParseDsn6 };
+export { ParseDx };
 export { ImportString };
 export { ImportJson };
 export { ParseJson };
@@ -266,7 +268,7 @@ const ParseCube = PluginStateTransform.BuiltIn({
 })({
     apply({ a }) {
         return Task.create('Parse Cube', async ctx => {
-            const parsed = await parseCube(a.data).runInContext(ctx);
+            const parsed = await parseCube(a.data, a.label).runInContext(ctx);
             if (parsed.isError) throw new Error(parsed.message);
             return new SO.Format.Cube(parsed.result);
         });
@@ -314,7 +316,7 @@ const ParseCcp4 = PluginStateTransform.BuiltIn({
 })({
     apply({ a }) {
         return Task.create('Parse CCP4/MRC/MAP', async ctx => {
-            const parsed = await CCP4.parse(a.data).runInContext(ctx);
+            const parsed = await CCP4.parse(a.data, a.label).runInContext(ctx);
             if (parsed.isError) throw new Error(parsed.message);
             return new SO.Format.Ccp4(parsed.result);
         });
@@ -330,13 +332,29 @@ const ParseDsn6 = PluginStateTransform.BuiltIn({
 })({
     apply({ a }) {
         return Task.create('Parse DSN6/BRIX', async ctx => {
-            const parsed = await DSN6.parse(a.data).runInContext(ctx);
+            const parsed = await DSN6.parse(a.data, a.label).runInContext(ctx);
             if (parsed.isError) throw new Error(parsed.message);
             return new SO.Format.Dsn6(parsed.result);
         });
     }
 });
 
+type ParseDx = typeof ParseDx
+const ParseDx = PluginStateTransform.BuiltIn({
+    name: 'parse-dx',
+    display: { name: 'Parse DX', description: 'Parse DX from Binary/String data' },
+    from: [SO.Data.Binary, SO.Data.String],
+    to: SO.Format.Dx
+})({
+    apply({ a }) {
+        return Task.create('Parse DX', async ctx => {
+            const parsed = await parseDx(a.data, a.label).runInContext(ctx);
+            if (parsed.isError) throw new Error(parsed.message);
+            return new SO.Format.Dx(parsed.result);
+        });
+    }
+});
+
 type ImportString = typeof ImportString
 const ImportString = PluginStateTransform.BuiltIn({
     name: 'import-string',

+ 9 - 10
src/mol-plugin-state/transforms/volume.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -14,7 +14,6 @@ import { Task } from '../../mol-task';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { PluginStateObject as SO, PluginStateTransform } from '../objects';
 import { volumeFromCube } from '../../mol-model-formats/volume/cube';
-import { parseDx } from '../../mol-io/reader/dx/parser';
 import { volumeFromDx } from '../../mol-model-formats/volume/dx';
 import { VolumeData } from '../../mol-model/volume';
 import { PluginContext } from '../../mol-plugin/context';
@@ -42,7 +41,7 @@ const VolumeFromCcp4 = PluginStateTransform.BuiltIn({
 })({
     apply({ a, params }) {
         return Task.create('Create volume from CCP4/MRC/MAP', async ctx => {
-            const volume = await volumeFromCcp4(a.data, { ...params, label: a.label }).runInContext(ctx);
+            const volume = await volumeFromCcp4(a.data, { ...params, label: a.data.name || a.label }).runInContext(ctx);
             const props = { label: volume.label || 'Volume', description: 'Volume' };
             return new SO.Volume.Data(volume, props);
         });
@@ -63,7 +62,7 @@ const VolumeFromDsn6 = PluginStateTransform.BuiltIn({
 })({
     apply({ a, params }) {
         return Task.create('Create volume from DSN6/BRIX', async ctx => {
-            const volume = await volumeFromDsn6(a.data, { ...params, label: a.label }).runInContext(ctx);
+            const volume = await volumeFromDsn6(a.data, { ...params, label: a.data.name || a.label }).runInContext(ctx);
             const props = { label: volume.label || 'Volume', description: 'Volume' };
             return new SO.Volume.Data(volume, props);
         });
@@ -85,7 +84,7 @@ const VolumeFromCube = PluginStateTransform.BuiltIn({
 })({
     apply({ a, params }) {
         return Task.create('Create volume from Cube', async ctx => {
-            const volume = await volumeFromCube(a.data, { ...params, label: a.label }).runInContext(ctx);
+            const volume = await volumeFromCube(a.data, { ...params, label: a.data.name || a.label }).runInContext(ctx);
             const props = { label: volume.label || 'Volume', description: 'Volume' };
             return new SO.Volume.Data(volume, props);
         });
@@ -95,15 +94,15 @@ const VolumeFromCube = PluginStateTransform.BuiltIn({
 type VolumeFromDx = typeof VolumeFromDx
 const VolumeFromDx = PluginStateTransform.BuiltIn({
     name: 'volume-from-dx',
-    display: { name: 'Parse PX', description: 'Parse DX string/binary and create volume.' },
-    from: [SO.Data.String, SO.Data.Binary],
+    display: { name: 'Parse DX', description: 'Create volume from DX data.' },
+    from: SO.Format.Dx,
     to: SO.Volume.Data
 })({
     apply({ a }) {
         return Task.create('Parse DX', async ctx => {
-            const parsed = await parseDx(a.data).runInContext(ctx);
-            if (parsed.isError) throw new Error(parsed.message);
-            const volume = await volumeFromDx(parsed.result, { label: a.label }).runInContext(ctx);
+            console.log(a);
+            const volume = await volumeFromDx(a.data, { label: a.data.name || a.label }).runInContext(ctx);
+            console.log(volume);
             const props = { label: volume.label || 'Volume', description: 'Volume' };
             return new SO.Volume.Data(volume, props);
         });

+ 8 - 2
src/mol-theme/label.ts

@@ -56,10 +56,16 @@ export function lociLabel(loci: Loci, options: Partial<LabelOptions> = {}): stri
         case 'cell-loci':
             const size = OrderedSet.size(loci.indices);
             const start = OrderedSet.start(loci.indices);
-            return [
+            const absVal = VolumeIsoValue.absolute(loci.volume.data.data[start]);
+            const relVal = VolumeIsoValue.toRelative(absVal, loci.volume.dataStats);
+            const label = [
                 `${loci.volume.label || 'Volume'}`,
                 `${size === 1 ? `Cell #${start}` : `${size} Cells`}`
-            ].join(' | ');
+            ];
+            if (size === 1) {
+                label.push(`${VolumeIsoValue.toString(absVal)} (${VolumeIsoValue.toString(relVal)})`);
+            }
+            return label.join(' | ');
     }
 }