diff --git a/src/mol-io/writer/cif/encoder.ts b/src/mol-io/writer/cif/encoder.ts index 2d2b5057eb24c4645b50f9cf55ea6ea565e36a36..f3d4b585753df0eee9c410d048e2ec2ad5fe9389 100644 --- a/src/mol-io/writer/cif/encoder.ts +++ b/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> { diff --git a/src/mol-io/writer/cif/encoder/binary.ts b/src/mol-io/writer/cif/encoder/binary.ts index 4e369e7cc394a2334c464ba3f9c1de5e3b47f6c0..762592b131eb0e9c966da65a3b8a71156cfb3d4d 100644 --- a/src/mol-io/writer/cif/encoder/binary.ts +++ b/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); } diff --git a/src/mol-io/writer/cif/encoder/text.ts b/src/mol-io/writer/cif/encoder/text.ts index 9ded26f16babe9f20cb9f6fd825e69c17c616533..318eb74f146652b516b5cfa131528317ddc05690 100644 --- a/src/mol-io/writer/cif/encoder/text.ts +++ b/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); } diff --git a/src/servers/volume/server/query/encode.ts b/src/servers/volume/server/query/encode.ts index 67277cccb2f2db4e10fac1c5791aeaf366cdbf1c..525e365ea1de97ed074abf06249a0d897c52628a 100644 --- a/src/servers/volume/server/query/encode.ts +++ b/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,