Browse Source

CIF encoder formatting

David Sehnal 6 years ago
parent
commit
fa02bb1178

+ 9 - 9
src/mol-io/writer/cif/encoder.ts

@@ -9,6 +9,7 @@ import Iterator from 'mol-data/iterator'
 import { Column, Table } from 'mol-data/db'
 import { Tensor } from 'mol-math/linear-algebra'
 import Encoder from '../encoder'
+import { ArrayEncoder, ArrayEncoding } from '../../common/binary-cif';
 
 // TODO: support for "coordinate fields", make "coordinate precision" a parameter of the encoder
 // TODO: automatically detect "precision" of floating point arrays.
@@ -28,21 +29,19 @@ export const enum FieldType {
 export interface FieldDefinitionBase<Key, Data> {
     name: string,
     valueKind?: (key: Key, data: Data) => Column.ValueKind,
-    // TODO:
-    // shouldInclude?: (data: Data) => boolean
+    encoder?: ArrayEncoder
 }
 
 export type FieldDefinition<Key = any, Data = any> =
     | FieldDefinitionBase<Key, Data> & { type: FieldType.Str, value(key: Key, data: Data): string }
-    | FieldDefinitionBase<Key, Data> & { type: FieldType.Int, value(key: Key, data: Data): number }
-    | FieldDefinitionBase<Key, Data> & { type: FieldType.Float, value(key: Key, data: Data): number }
+    | FieldDefinitionBase<Key, Data> & { type: FieldType.Int, value(key: Key, data: Data): number, typedArray?: ArrayEncoding.TypedArrayCtor }
+    | FieldDefinitionBase<Key, Data> & { type: FieldType.Float, value(key: Key, data: Data): number, digitCount?: number, typedArray?: ArrayEncoding.TypedArrayCtor }
 
 export interface FieldFormat {
     // TODO: do we actually need this?
-    // textDecimalPlaces: number,
-    // stringEncoder: ArrayEncoder,
-    // numericEncoder: ArrayEncoder,
-    // typedArray?: E.TypedArrayCtor
+    // digitCount?: number,
+    // encoder?: ArrayEncoder,
+    // typedArray?: ArrayEncoding.TypedArrayCtor
 }
 
 export namespace FieldFormat {
@@ -55,7 +54,8 @@ export namespace FieldFormat {
 
 export interface CategoryDefinition<Key = any, Data = any> {
     name: string,
-    fields: FieldDefinition<Key, Data>[]
+    fields: FieldDefinition<Key, Data>[],
+    format?: { [name: string]: FieldFormat }
 }
 
 export interface CategoryInstance<Key = any, Data = any> {

+ 14 - 8
src/mol-io/writer/cif/encoder/binary.ts

@@ -77,16 +77,22 @@ export default class BinaryCIFWriter<Context> implements CIFEncoder<Uint8Array,
     }
 }
 
-function encodeField(field: FieldDefinition, data: { data: any, keys: () => Iterator<any> }[], totalCount: number, format: FieldFormat): EncodedColumn {
-    const isStr = field.type === FieldType.Str
-    let array: any[], encoder: ArrayEncoder;
+function createArray(field: FieldDefinition, count: number) {
+    if (field.type === FieldType.Str) return new Array(count) as any;
+    else if (field.typedArray) return new field.typedArray(count) as any;
+    else return (field.type === FieldType.Int ? new Int32Array(count) : new Float32Array(count)) as any;
+}
 
-    if (isStr) {
-        array = new Array(totalCount);
-        encoder = ArrayEncoder.by(E.stringArray); //format.stringEncoder;
+function encodeField(field: FieldDefinition, data: { data: any, keys: () => Iterator<any> }[], totalCount: number, format: FieldFormat): EncodedColumn {
+    const isStr = field.type === FieldType.Str;
+    const array = createArray(field, totalCount);
+    let encoder: ArrayEncoder;
+
+    if (field.encoder) {
+        encoder = field.encoder;
+    } else if (isStr) {
+        encoder = ArrayEncoder.by(E.stringArray);
     } else {
-        //array = format.typedArray ? new format.typedArray(totalCount) as any : field.type === FieldType.Int ? new Int32Array(totalCount) : new Float32Array(totalCount);
-        array = (field.type === FieldType.Int ? new Int32Array(totalCount) : new Float32Array(totalCount)) as any;
         encoder = ArrayEncoder.by(E.byteArray);
     }
 

+ 15 - 4
src/mol-io/writer/cif/encoder/text.ts

@@ -60,7 +60,7 @@ export default class TextCIFEncoder<Context> implements Enc.CIFEncoder<string, C
     }
 }
 
-function writeValue(builder: StringBuilder, data: any, key: any, f: Enc.FieldDefinition<any, any>): boolean {
+function writeValue(builder: StringBuilder, data: any, key: any, f: Enc.FieldDefinition<any, any>, floatPrecision: number): boolean {
     const kind = f.valueKind;
     const p = kind ? kind(key, data) : Column.ValueKind.Present;
     if (p !== Column.ValueKind.Present) {
@@ -79,12 +79,20 @@ function writeValue(builder: StringBuilder, data: any, key: any, f: Enc.FieldDef
         } else if (t === Enc.FieldType.Int) {
             writeInteger(builder, val as number);
         } else {
-            writeFloat(builder, val as number, 1000000);
+            writeFloat(builder, val as number, floatPrecision);
         }
     }
     return false;
 }
 
+function getFloatPrecisions(cat: Enc.CategoryInstance) {
+    const ret: number[] = [];
+    for (const f of cat.definition.fields) {
+        ret[ret.length] = f.type === Enc.FieldType.Float ? Math.pow(10, typeof f.digitCount === 'undefined' ? 6 : f.digitCount) : 0;
+    }
+    return ret;
+}
+
 function writeCifSingleRecord(category: Enc.CategoryInstance<any>, builder: StringBuilder) {
     const fields = category.definition.fields;
     const data = category.data;
@@ -93,10 +101,12 @@ function writeCifSingleRecord(category: Enc.CategoryInstance<any>, builder: Stri
     const it = category.keys();
     const key = it.move();
 
+    const precisions = getFloatPrecisions(category);
+
     for (let _f = 0; _f < fields.length; _f++) {
         const f = fields[_f];
         StringBuilder.writePadRight(builder, `_${category.definition.name}.${f.name}`, width);
-        const multiline = writeValue(builder, data, key, f);
+        const multiline = writeValue(builder, data, key, f, precisions[_f]);
         if (!multiline) StringBuilder.newline(builder);
     }
     StringBuilder.write(builder, '#\n');
@@ -106,6 +116,7 @@ function writeCifLoop(categories: Enc.CategoryInstance[], builder: StringBuilder
     const first = categories[0];
     const fields = first.definition.fields;
     const fieldCount = fields.length;
+    const precisions = getFloatPrecisions(first);
 
     writeLine(builder, 'loop_');
     for (let i = 0; i < fieldCount; i++) {
@@ -124,7 +135,7 @@ function writeCifLoop(categories: Enc.CategoryInstance[], builder: StringBuilder
 
             let multiline = false;
             for (let _f = 0; _f < fieldCount; _f++) {
-                multiline = writeValue(builder, data, key, fields[_f]);
+                multiline = writeValue(builder, data, key, fields[_f], precisions[_f]);
             }
             if (!multiline) StringBuilder.newline(builder);
         }

+ 30 - 32
src/servers/volume/server/query/encode.ts

@@ -13,7 +13,7 @@ import VERSION from '../version'
 import * as DataFormat from '../../common/data-format'
 import { Column } from 'mol-data/db';
 import { Iterator } from 'mol-data';
-//import { ArrayEncoding, ArrayEncoder } from 'mol-io/common/binary-cif';
+import { ArrayEncoding, ArrayEncoder } from 'mol-io/common/binary-cif';
 
 export default function encode(query: Data.QueryContext, output: Data.QueryOutputStream) {
     let w = Encoder.create({ binary: query.params.asBinary, encoderName: `VolumeServer ${VERSION}` });
@@ -45,8 +45,8 @@ function int32<T>(name: string, value: (data: T) => number): FieldDesc<T> {
     return { name, type: Encoder.FieldType.Int, value: (i, d) => value(d) };
 }
 
-function float64<T>(name: string, value: (data: T) => number, precisionMultiplier: number = 1000000): FieldDesc<T> {
-    return { name, type: Encoder.FieldType.Float, value: (i, d) => value(d) };
+function float64<T>(name: string, value: (data: T) => number, digitCount: number = 6): FieldDesc<T> {
+    return { name, type: Encoder.FieldType.Float, value: (i, d) => value(d), digitCount };
 }
 
 interface _vd3d_Ctx {
@@ -80,13 +80,13 @@ const _volume_data_3d_info_fields: FieldDesc<_vd3d_Ctx>[] = [
 
     int32<_vd3d_Ctx>('spacegroup_number', ctx => ctx.header.spacegroup.number),
 
-    float64<_vd3d_Ctx>('spacegroup_cell_size[0]', ctx => ctx.header.spacegroup.size[0], 1000),
-    float64<_vd3d_Ctx>('spacegroup_cell_size[1]', ctx => ctx.header.spacegroup.size[1], 1000),
-    float64<_vd3d_Ctx>('spacegroup_cell_size[2]', ctx => ctx.header.spacegroup.size[2], 1000),
+    float64<_vd3d_Ctx>('spacegroup_cell_size[0]', ctx => ctx.header.spacegroup.size[0], 3),
+    float64<_vd3d_Ctx>('spacegroup_cell_size[1]', ctx => ctx.header.spacegroup.size[1], 3),
+    float64<_vd3d_Ctx>('spacegroup_cell_size[2]', ctx => ctx.header.spacegroup.size[2], 3),
 
-    float64<_vd3d_Ctx>('spacegroup_cell_angles[0]', ctx => ctx.header.spacegroup.angles[0], 1000),
-    float64<_vd3d_Ctx>('spacegroup_cell_angles[1]', ctx => ctx.header.spacegroup.angles[1], 1000),
-    float64<_vd3d_Ctx>('spacegroup_cell_angles[2]', ctx => ctx.header.spacegroup.angles[2], 1000),
+    float64<_vd3d_Ctx>('spacegroup_cell_angles[0]', ctx => ctx.header.spacegroup.angles[0], 3),
+    float64<_vd3d_Ctx>('spacegroup_cell_angles[1]', ctx => ctx.header.spacegroup.angles[1], 3),
+    float64<_vd3d_Ctx>('spacegroup_cell_angles[2]', ctx => ctx.header.spacegroup.angles[2], 3),
 
     float64<_vd3d_Ctx>('mean_source', ctx => ctx.globalValuesInfo.mean),
     float64<_vd3d_Ctx>('mean_sampled', ctx => ctx.sampledValuesInfo.mean),
@@ -123,29 +123,27 @@ function _volume_data_3d_number(i: number, ctx: DataFormat.ValueArray): number {
 function _volume_data_3d(ctx: ResultContext) {
     const data = ctx.query.values[ctx.channelIndex];
 
-    // const E = ArrayEncoding;
-    // let encoder: ArrayEncoder;
-    // let typedArray: any;
-    // if (ctx.query.data.header.valueType === DataFormat.ValueType.Float32 || ctx.query.data.header.valueType === DataFormat.ValueType.Int16) {
-    //     let min: number, max: number;
-    //     min = data[0], max = data[0];
-    //     for (let i = 0, n = data.length; i < n; i++) {
-    //         let v = data[i];
-    //         if (v < min) min = v;
-    //         else if (v > max) max = v;
-    //     }
-    //     typedArray = Float32Array;
-    //     // encode into 255 steps and store each value in 1 byte.
-    //     encoder = E.by(E.intervalQuantizaiton(min, max, 255, Uint8Array)).and(E.byteArray);
-    // } else {
-    //     typedArray = Int8Array;
-    //     // just encode the bytes
-    //     encoder = E.by(E.byteArray)
-    // }
-
-    let fields: FieldDesc<typeof data>[] = [{
-        name: 'values', type: Encoder.FieldType.Float, value: _volume_data_3d_number
-    }];
+    const E = ArrayEncoding;
+    let encoder: ArrayEncoder;
+    let typedArray: any;
+    if (ctx.query.data.header.valueType === DataFormat.ValueType.Float32 || ctx.query.data.header.valueType === DataFormat.ValueType.Int16) {
+        let min: number, max: number;
+        min = data[0], max = data[0];
+        for (let i = 0, n = data.length; i < n; i++) {
+            let v = data[i];
+            if (v < min) min = v;
+            else if (v > max) max = v;
+        }
+        typedArray = Float32Array;
+        // encode into 255 steps and store each value in 1 byte.
+        encoder = E.by(E.intervalQuantizaiton(min, max, 255, Uint8Array)).and(E.byteArray);
+    } else {
+        typedArray = Int8Array;
+        // just encode the bytes
+        encoder = E.by(E.byteArray)
+    }
+
+    let fields: FieldDesc<typeof data>[] = [{ name: 'values', type: Encoder.FieldType.Float, value: _volume_data_3d_number, encoder, typedArray, digitCount: 6 }];
 
     return {
         data,