diff --git a/src/apps/cif2bcif/converter.ts b/src/apps/cif2bcif/converter.ts index 06a9ed517a6529752436d7a22ef9e4908abc80c0..05dea6be34b9f0ed23a8a80f65980cee4054b054 100644 --- a/src/apps/cif2bcif/converter.ts +++ b/src/apps/cif2bcif/converter.ts @@ -4,9 +4,8 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import Iterator from 'mol-data/iterator' import CIF, { CifCategory } from 'mol-io/reader/cif' -import * as Encoder from 'mol-io/writer/cif' +import { CIFCategory, createCIFEncoder } from 'mol-io/writer/cif' import * as fs from 'fs' import classify from './field-classifier' @@ -19,19 +18,12 @@ async function getCIF(path: string) { return parsed.result; } -function createDefinition(cat: CifCategory): Encoder.CategoryDefinition { - return { - name: cat.name, - fields: cat.fieldNames.map(n => classify(n, cat.getField(n)!)) - } -} - -function getCategoryInstanceProvider(cat: CifCategory): Encoder.CategoryProvider { +function getCategoryInstanceProvider(cat: CifCategory): CIFCategory.Provider { return function (ctx: any) { return { data: cat, - definition: createDefinition(cat), - keys: () => Iterator.Range(0, cat.rowCount - 1), + name: cat.name, + fields: cat.fieldNames.map(n => classify(n, cat.getField(n)!)), rowCount: cat.rowCount }; } @@ -40,7 +32,7 @@ function getCategoryInstanceProvider(cat: CifCategory): Encoder.CategoryProvider export default async function convert(path: string, asText = false) { const cif = await getCIF(path); - const encoder = Encoder.create({ binary: !asText, encoderName: 'mol* cif2bcif' }); + const encoder = createCIFEncoder({ binary: !asText, encoderName: 'mol* cif2bcif' }); for (const b of cif.blocks) { encoder.startDataBlock(b.header); for (const c of b.categoryNames) { diff --git a/src/apps/cif2bcif/field-classifier.ts b/src/apps/cif2bcif/field-classifier.ts index d0725993f29c05f1326ce2ffead2a45ec0083ab5..6d4273672dcfacc0ce04da04e3e963c1b281d017 100644 --- a/src/apps/cif2bcif/field-classifier.ts +++ b/src/apps/cif2bcif/field-classifier.ts @@ -6,14 +6,14 @@ import { Column } from 'mol-data/db' import { CifField } from 'mol-io/reader/cif/data-model' -import { FieldDefinition, FieldType } from 'mol-io/writer/cif/encoder' +import { CIFField } from 'mol-io/writer/cif' const intRegex = /^-?\d+$/ const floatRegex = /^-?(([0-9]+)[.]?|([0-9]*[.][0-9]+))([(][0-9]+[)])?([eE][+-]?[0-9]+)?/ // Classify a cif field as str, int or float based the data it contains. // To classify a field as int or float all items are checked. -function classify(name: string, field: CifField): FieldDefinition { +function classify(name: string, field: CifField): CIFField { let floatCount = 0, hasString = false; for (let i = 0, _i = field.rowCount; i < _i; i++) { const k = field.valueKind(i); @@ -24,9 +24,9 @@ function classify(name: string, field: CifField): FieldDefinition { else { hasString = true; break; } } - if (hasString) return { name, type: FieldType.Str, value: field.str, valueKind: field.valueKind }; - if (floatCount > 0) return { name, type: FieldType.Float, value: field.float, valueKind: field.valueKind }; - return { name, type: FieldType.Int, value: field.int, valueKind: field.valueKind }; + if (hasString) return { name, type: CIFField.Type.Str, value: field.str, valueKind: field.valueKind }; + if (floatCount > 0) return { name, type: CIFField.Type.Float, value: field.float, valueKind: field.valueKind }; + return { name, type: CIFField.Type.Int, value: field.int, valueKind: field.valueKind }; } export default classify; \ No newline at end of file diff --git a/src/apps/combine-mmcif/index.ts b/src/apps/combine-mmcif/index.ts index 205c55c11c748d752dd12033405a524fd6102b22..11e8394a67d510b2b053acddf4b9d0e642f5384a 100644 --- a/src/apps/combine-mmcif/index.ts +++ b/src/apps/combine-mmcif/index.ts @@ -17,7 +17,7 @@ import { Progress } from 'mol-task' import { Database, Table, DatabaseCollection } from 'mol-data/db' import CIF from 'mol-io/reader/cif' // import { CCD_Schema } from 'mol-io/reader/cif/schema/ccd' -import * as Encoder from 'mol-io/writer/cif' +import { CIFEncoder, createCIFEncoder } from 'mol-io/writer/cif' import { mmCIF_Schema, mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif'; import { CCD_Schema } from 'mol-io/reader/cif/schema/ccd'; import { BIRD_Schema } from 'mol-io/reader/cif/schema/bird'; @@ -85,8 +85,8 @@ async function parseCif(data: string|Uint8Array) { } export function getEncodedCif(name: string, database: Database<Database.Schema>, binary = false) { - const encoder = Encoder.create({ binary, encoderName: 'mol*' }); - Encoder.writeDatabase(encoder, name, database) + const encoder = createCIFEncoder({ binary, encoderName: 'mol*' }); + CIFEncoder.writeDatabase(encoder, name, database) return encoder.getData(); } diff --git a/src/apps/domain-annotation-server/mapping.ts b/src/apps/domain-annotation-server/mapping.ts index a241b4f90c3319cf77503192853624d5380019f0..14be8b6c352aee344bb17ec45457d840731ab28e 100644 --- a/src/apps/domain-annotation-server/mapping.ts +++ b/src/apps/domain-annotation-server/mapping.ts @@ -5,7 +5,7 @@ */ import { Table } from 'mol-data/db' -import { EncoderInstance, create as createEncoder } from 'mol-io/writer/cif' +import { CIFEncoder, createCIFEncoder as createEncoder } from 'mol-io/writer/cif' import * as S from './schemas' import { getCategoryInstanceProvider } from './utils' @@ -36,7 +36,7 @@ interface DomainAnnotation { } type MappingRow = Table.Row<S.mapping>; -function writeDomain(enc: EncoderInstance, domain: DomainAnnotation | undefined) { +function writeDomain(enc: CIFEncoder, domain: DomainAnnotation | undefined) { if (!domain) return; enc.writeCategory(getCategoryInstanceProvider(`pdbx_${domain.name}_domain_annotation`, domain.domains)); enc.writeCategory(getCategoryInstanceProvider(`pdbx_${domain.name}_domain_mapping`, domain.mappings)); diff --git a/src/apps/domain-annotation-server/utils.ts b/src/apps/domain-annotation-server/utils.ts index de3e40b92592a2995294eb77eca27dafdcbe555d..0a689681df00b73c39f6dc54e61892681be28fa7 100644 --- a/src/apps/domain-annotation-server/utils.ts +++ b/src/apps/domain-annotation-server/utils.ts @@ -5,8 +5,7 @@ */ import { Table } from 'mol-data/db' -import Iterator from 'mol-data/iterator' -import * as Encoder from 'mol-io/writer/cif' +import { CIFField, CIFCategory } from 'mol-io/writer/cif' function columnValue(k: string) { return (i: number, d: any) => d[k].value(i); @@ -17,25 +16,21 @@ function columnValueKind(k: string) { } function ofSchema(schema: Table.Schema) { - const fields: Encoder.FieldDefinition[] = []; + const fields: CIFField[] = []; for (const k of Object.keys(schema)) { const t = schema[k]; - const type: any = t.valueType === 'str' ? Encoder.FieldType.Str : t.valueType === 'int' ? Encoder.FieldType.Int : Encoder.FieldType.Float; + const type: any = t.valueType === 'str' ? CIFField.Type.Str : t.valueType === 'int' ? CIFField.Type.Int : CIFField.Type.Float; fields.push({ name: k, type, value: columnValue(k), valueKind: columnValueKind(k) }) } return fields; } -function ofTable<S extends Table.Schema>(name: string, table: Table<S>): Encoder.CategoryDefinition<number> { - return { name, fields: ofSchema(table._schema) } -} - -export function getCategoryInstanceProvider(name: string, table: Table<any>): Encoder.CategoryProvider { +export function getCategoryInstanceProvider(name: string, table: Table<any>): CIFCategory.Provider { return () => { return { data: table, - definition: ofTable(name, table), - keys: () => Iterator.Range(0, table._rowCount - 1), + name, + fields: ofSchema(table._schema), rowCount: table._rowCount }; } diff --git a/src/mol-io/reader/cif/schema/mmcif.ts b/src/mol-io/reader/cif/schema/mmcif.ts index 8f67d33bfa2b3c7ff56c2358ba004197450eb8ca..283b713dc4fbc75cdf06e1584e205b50fdfcefe3 100644 --- a/src/mol-io/reader/cif/schema/mmcif.ts +++ b/src/mol-io/reader/cif/schema/mmcif.ts @@ -80,11 +80,11 @@ export const mmCIF_Schema = { pdbx_aromatic_flag: Aliased<'Y' | 'N'>(str), }, entity: { - details: str, - formula_weight: float, id: str, src_method: Aliased<'nat' | 'man' | 'syn'>(str), type: Aliased<'polymer' | 'non-polymer' | 'macrolide' | 'water'>(str), + details: str, + formula_weight: float, pdbx_description: str, pdbx_number_of_molecules: float, pdbx_mutation: str, diff --git a/src/mol-io/writer/cif.ts b/src/mol-io/writer/cif.ts index 6dedd27e88abb6f1767675954feefe87126b7131..585e4c147c6ab6df54e751071e630b548b661f24 100644 --- a/src/mol-io/writer/cif.ts +++ b/src/mol-io/writer/cif.ts @@ -5,32 +5,13 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Database, DatabaseCollection } from 'mol-data/db' - import TextCIFEncoder from './cif/encoder/text' import BinaryCIFEncoder from './cif/encoder/binary' -import { CategoryDefinition } from './cif/encoder' +import { CIFEncoder } from './cif/encoder' export * from './cif/encoder' -export type EncoderInstance = BinaryCIFEncoder<{}> | TextCIFEncoder<{}> - -export function create(params?: { binary?: boolean, encoderName?: string }): EncoderInstance { +export function createCIFEncoder(params?: { binary?: boolean, encoderName?: string }): CIFEncoder { const { binary = false, encoderName = 'mol*' } = params || {}; return binary ? new BinaryCIFEncoder(encoderName) : new TextCIFEncoder(); -} - -export function writeDatabase(encoder: EncoderInstance, name: string, database: Database<Database.Schema>) { - encoder.startDataBlock(name); - for (const table of database._tableNames) { - encoder.writeCategory( - CategoryDefinition.instanceProviderOfTable(table, database[table]) - ); - } -} - -export function writeDatabaseCollection(encoder: EncoderInstance, collection: DatabaseCollection<Database.Schema>) { - for (const name of Object.keys(collection)) { - writeDatabase(encoder, name, collection[name]) - } } \ No newline at end of file diff --git a/src/mol-io/writer/cif/encoder.ts b/src/mol-io/writer/cif/encoder.ts index f3d4b585753df0eee9c410d048e2ec2ad5fe9389..ee41abff426a3b070befd6719dc1aa675ccf7191 100644 --- a/src/mol-io/writer/cif/encoder.ts +++ b/src/mol-io/writer/cif/encoder.ts @@ -6,7 +6,7 @@ */ import Iterator from 'mol-data/iterator' -import { Column, Table } from 'mol-data/db' +import { Column, Table, Database, DatabaseCollection } from 'mol-data/db' import { Tensor } from 'mol-math/linear-algebra' import Encoder from '../encoder' import { ArrayEncoder, ArrayEncoding } from '../../common/binary-cif'; @@ -17,65 +17,94 @@ import { ArrayEncoder, ArrayEncoding } from '../../common/binary-cif'; // TODO: add "repeat encoding"? [[1, 2], [1, 2], [1, 2]] --- Repeat ---> [[1, 2], 3] // TODO: Add "higher level fields"? (i.e. generalization of repeat) // TODO: align "data blocks" to 8 byte offsets for fast typed array windows? (prolly needs some testing if this is actually the case too) -// TODO: "parametric encoders"? Specify encoding as [{ param: 'value1', encoding1 }, { param: 'value2', encoding2 }] -// then the encoder can specify { param: 'value1' } and the correct encoding will be used. -// Use case: variable precision encoding for different fields. -// Perhaps implement this as parameter spaces... -export const enum FieldType { - Str, Int, Float -} - -export interface FieldDefinitionBase<Key, Data> { +export interface CIFField<Key = any, Data = any> { name: string, + type: CIFField.Type, valueKind?: (key: Key, data: Data) => Column.ValueKind, - encoder?: ArrayEncoder + defaultFormat?: CIFField.Format, + value(key: Key, data: Data): string | number } -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, typedArray?: ArrayEncoding.TypedArrayCtor } - | FieldDefinitionBase<Key, Data> & { type: FieldType.Float, value(key: Key, data: Data): number, digitCount?: number, typedArray?: ArrayEncoding.TypedArrayCtor } +export namespace CIFField { + export const enum Type { Str, Int, Float } -export interface FieldFormat { - // TODO: do we actually need this? - // digitCount?: number, - // encoder?: ArrayEncoder, - // typedArray?: ArrayEncoding.TypedArrayCtor -} + export interface Format { + digitCount?: number, + encoder?: ArrayEncoder, + typedArray?: ArrayEncoding.TypedArrayCtor + } -export namespace FieldFormat { - export const Default: FieldFormat = { - // textDecimalPlaces: 3, - // stringEncoder: ArrayEncoder.by(E.stringArray), - // numericEncoder: ArrayEncoder.by(E.byteArray) - }; -} + export function getDigitCount(field: CIFField) { + if (field.defaultFormat && typeof field.defaultFormat.digitCount !== 'undefined') return field.defaultFormat.digitCount; + return 6; + } -export interface CategoryDefinition<Key = any, Data = any> { - name: string, - fields: FieldDefinition<Key, Data>[], - format?: { [name: string]: FieldFormat } + export function str<K, D = any>(name: string, value: (k: K, d: D) => string, params?: { valueKind?: (k: K, d: D) => Column.ValueKind, encoder?: ArrayEncoder }): CIFField<K, D> { + return { name, type: Type.Str, value, valueKind: params && params.valueKind, defaultFormat: params && params.encoder ? { encoder: params.encoder } : void 0 }; + } + + export function int<K, D = any>(name: string, value: (k: K, d: D) => number, params?: { valueKind?: (k: K, d: D) => Column.ValueKind, encoder?: ArrayEncoder, typedArray?: ArrayEncoding.TypedArrayCtor }): CIFField<K, D> { + return { + name, + type: Type.Int, + value, + valueKind: params && params.valueKind, + defaultFormat: params ? { encoder: params.encoder, typedArray: params.typedArray } : void 0 }; + } + + export function float<K, D = any>(name: string, value: (k: K, d: D) => number, params?: { valueKind?: (k: K, d: D) => Column.ValueKind, encoder?: ArrayEncoder, typedArray?: ArrayEncoding.TypedArrayCtor, digitCount?: number }): CIFField<K, D> { + return { + name, + type: Type.Float, + value, + valueKind: params && params.valueKind, + defaultFormat: params ? { encoder: params.encoder, typedArray: params.typedArray, digitCount: typeof params.digitCount !== 'undefined' ? params.digitCount : void 0 } : void 0 + }; + } } -export interface CategoryInstance<Key = any, Data = any> { - data: Data, - definition: CategoryDefinition<Key, Data>, - formats?: { [name: string]: Partial<FieldFormat> }, +export interface CIFCategory<Key = any, Data = any> { + name: string, + fields: CIFField<Key, Data>[], + data?: Data, rowCount: number, - keys(): Iterator<Key> + keys?: () => Iterator<Key> } -export interface CategoryProvider { - (ctx: any): CategoryInstance +export namespace CIFCategory { + export interface Provider<Ctx = any> { + (ctx: Ctx): CIFCategory + } + + export function ofTable(name: string, table: Table<Table.Schema>): CIFCategory<number, Table<Table.Schema>> { + return { name, fields: cifFieldsFromTableSchema(table._schema), data: table, rowCount: table._rowCount }; + } } -export interface CIFEncoder<T = string | Uint8Array, Context = any> extends Encoder { +export interface CIFEncoder<T = string | Uint8Array> extends Encoder { + // setFormatter(): void, startDataBlock(header: string): void, - writeCategory(category: CategoryProvider, contexts?: Context[]): void, + writeCategory<Ctx>(category: CIFCategory.Provider<Ctx>, contexts?: Ctx[]): void, getData(): T } +export namespace CIFEncoder { + export function writeDatabase(encoder: CIFEncoder, name: string, database: Database<Database.Schema>) { + encoder.startDataBlock(name); + for (const table of database._tableNames) { + encoder.writeCategory(() => CIFCategory.ofTable(table, database[table])); + } + } + + export function writeDatabaseCollection(encoder: CIFEncoder, collection: DatabaseCollection<Database.Schema>) { + for (const name of Object.keys(collection)) { + writeDatabase(encoder, name, collection[name]) + } + } +} + + function columnValue(k: string) { return (i: number, d: any) => d[k].value(i); } @@ -93,8 +122,8 @@ function columnValueKind(k: string) { } function getTensorDefinitions(field: string, space: Tensor.Space) { - const fieldDefinitions: FieldDefinition[] = [] - const type = FieldType.Float + const fieldDefinitions: CIFField[] = [] + const type = CIFField.Type.Float const valueKind = columnValueKind(field) if (space.rank === 1) { const rows = space.dimensions[0] @@ -126,42 +155,23 @@ function getTensorDefinitions(field: string, space: Tensor.Space) { return fieldDefinitions } -export namespace FieldDefinitions { - export function ofSchema(schema: Table.Schema) { - const fields: FieldDefinition[] = []; - for (const k of Object.keys(schema)) { - const t = schema[k]; - if (t.valueType === 'int') { - fields.push({ name: k, type: FieldType.Int, value: columnValue(k), valueKind: columnValueKind(k) }); - } else if (t.valueType === 'float') { - fields.push({ name: k, type: FieldType.Float, value: columnValue(k), valueKind: columnValueKind(k) }); - } else if (t.valueType === 'str') { - fields.push({ name: k, type: FieldType.Str, value: columnValue(k), valueKind: columnValueKind(k) }); - } else if (t.valueType === 'list') { - fields.push({ name: k, type: FieldType.Str, value: columnListValue(k), valueKind: columnValueKind(k) }) - } else if (t.valueType === 'tensor') { - fields.push(...getTensorDefinitions(k, t.space)) - } else { - throw new Error(`Unknown valueType ${t.valueType}`); - } - } - return fields; - } -} - -export namespace CategoryDefinition { - export function ofTable<S extends Table.Schema>(name: string, table: Table<S>): CategoryDefinition<number> { - return { name, fields: FieldDefinitions.ofSchema(table._schema) } - } - - export function instanceProviderOfTable(name: string, table: Table<Table.Schema>): CategoryProvider { - return function (ctx: any) { - return { - data: table, - definition: ofTable(name, table), - keys: () => Iterator.Range(0, table._rowCount - 1), - rowCount: table._rowCount - }; +function cifFieldsFromTableSchema(schema: Table.Schema) { + const fields: CIFField[] = []; + for (const k of Object.keys(schema)) { + const t = schema[k]; + if (t.valueType === 'int') { + fields.push({ name: k, type: CIFField.Type.Int, value: columnValue(k), valueKind: columnValueKind(k) }); + } else if (t.valueType === 'float') { + fields.push({ name: k, type: CIFField.Type.Float, value: columnValue(k), valueKind: columnValueKind(k) }); + } else if (t.valueType === 'str') { + fields.push({ name: k, type: CIFField.Type.Str, value: columnValue(k), valueKind: columnValueKind(k) }); + } else if (t.valueType === 'list') { + fields.push({ name: k, type: CIFField.Type.Str, value: columnListValue(k), valueKind: columnValueKind(k) }) + } else if (t.valueType === 'tensor') { + fields.push(...getTensorDefinitions(k, t.space)) + } else { + throw new Error(`Unknown valueType ${t.valueType}`); } } -} + return fields; +} \ No newline at end of file diff --git a/src/mol-io/writer/cif/encoder/binary.ts b/src/mol-io/writer/cif/encoder/binary.ts index 762592b131eb0e9c966da65a3b8a71156cfb3d4d..15429e97279b0fbe9768393b4a8092037d929c21 100644 --- a/src/mol-io/writer/cif/encoder/binary.ts +++ b/src/mol-io/writer/cif/encoder/binary.ts @@ -6,16 +6,16 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import Iterator from 'mol-data/iterator' +import { Iterator } from 'mol-data' import { Column } from 'mol-data/db' import encodeMsgPack from '../../../common/msgpack/encode' import { EncodedColumn, EncodedData, EncodedFile, EncodedDataBlock, EncodedCategory, ArrayEncoder, ArrayEncoding as E, VERSION } from '../../../common/binary-cif' -import { FieldDefinition, FieldFormat, FieldType, CategoryProvider, CIFEncoder } from '../encoder' +import { CIFField, CIFCategory, CIFEncoder } from '../encoder' import Writer from '../../writer' -export default class BinaryCIFWriter<Context> implements CIFEncoder<Uint8Array, Context> { +export default class BinaryCIFWriter implements CIFEncoder<Uint8Array> { private data: EncodedFile; private dataBlocks: EncodedDataBlock[] = []; private encodedData: Uint8Array; @@ -27,7 +27,7 @@ export default class BinaryCIFWriter<Context> implements CIFEncoder<Uint8Array, }); } - writeCategory(category: CategoryProvider, contexts?: Context[]) { + writeCategory<Ctx>(category: CIFCategory.Provider<Ctx>, contexts?: Ctx[]) { if (!this.data) { throw new Error('The writer contents have already been encoded, no more writing.'); } @@ -44,10 +44,10 @@ export default class BinaryCIFWriter<Context> implements CIFEncoder<Uint8Array, if (!count) return; const first = categories[0]!; - const cat: EncodedCategory = { name: '_' + first.definition.name, columns: [], rowCount: count }; - const data = categories.map(c => ({ data: c.data, keys: () => c.keys() })); - for (const f of first.definition.fields) { - cat.columns.push(encodeField(f, data, count, FieldFormat.Default)); + const cat: EncodedCategory = { name: '_' + first.name, columns: [], rowCount: count }; + const data = categories.map(c => ({ data: c.data, keys: () => c.keys ? c.keys() : Iterator.Range(0, c.rowCount - 1) })); + for (const f of first.fields) { + cat.columns.push(encodeField(f, data, count, f.defaultFormat)); } this.dataBlocks[this.dataBlocks.length - 1].categories.push(cat); } @@ -77,19 +77,19 @@ export default class BinaryCIFWriter<Context> implements CIFEncoder<Uint8Array, } } -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; +function createArray(field: CIFField, count: number) { + if (field.type === CIFField.Type.Str) return new Array(count) as any; + else if (field.defaultFormat && field.defaultFormat.typedArray) return new field.defaultFormat.typedArray(count) as any; + else return (field.type === CIFField.Type.Int ? new Int32Array(count) : new Float32Array(count)) as any; } -function encodeField(field: FieldDefinition, data: { data: any, keys: () => Iterator<any> }[], totalCount: number, format: FieldFormat): EncodedColumn { - const isStr = field.type === FieldType.Str; +function encodeField(field: CIFField, data: { data: any, keys: () => Iterator<any> }[], totalCount: number, format?: CIFField.Format): EncodedColumn { + const isStr = field.type === CIFField.Type.Str; const array = createArray(field, totalCount); let encoder: ArrayEncoder; - if (field.encoder) { - encoder = field.encoder; + if (field.defaultFormat && field.defaultFormat.encoder) { + encoder = field.defaultFormat.encoder; } else if (isStr) { encoder = ArrayEncoder.by(E.stringArray); } else { diff --git a/src/mol-io/writer/cif/encoder/text.ts b/src/mol-io/writer/cif/encoder/text.ts index 318eb74f146652b516b5cfa131528317ddc05690..fc49a7dd719da30cc4c226ea96b7fac4c3b3286c 100644 --- a/src/mol-io/writer/cif/encoder/text.ts +++ b/src/mol-io/writer/cif/encoder/text.ts @@ -6,12 +6,13 @@ * @author David Sehnal <david.sehnal@gmail.com> */ +import { Iterator } from 'mol-data' import { Column } from 'mol-data/db' import StringBuilder from 'mol-util/string-builder' -import * as Enc from '../encoder' +import { CIFCategory, CIFField, CIFEncoder } from '../encoder' import Writer from '../../writer' -export default class TextCIFEncoder<Context> implements Enc.CIFEncoder<string, Context> { +export default class TextCIFEncoder implements CIFEncoder<string> { private builder = StringBuilder.create(); private encoded = false; private dataBlockCreated = false; @@ -21,7 +22,7 @@ export default class TextCIFEncoder<Context> implements Enc.CIFEncoder<string, C StringBuilder.write(this.builder, `data_${(header || '').replace(/[ \n\t]/g, '').toUpperCase()}\n#\n`); } - writeCategory(category: Enc.CategoryProvider, contexts?: Context[]) { + writeCategory<Ctx>(category: CIFCategory.Provider<Ctx>, contexts?: Ctx[]) { if (this.encoded) { throw new Error('The writer contents have already been encoded, no more writing.'); } @@ -60,7 +61,7 @@ export default class TextCIFEncoder<Context> implements Enc.CIFEncoder<string, C } } -function writeValue(builder: StringBuilder, data: any, key: any, f: Enc.FieldDefinition<any, any>, floatPrecision: number): boolean { +function writeValue(builder: StringBuilder, data: any, key: any, f: CIFField<any, any>, floatPrecision: number): boolean { const kind = f.valueKind; const p = kind ? kind(key, data) : Column.ValueKind.Present; if (p !== Column.ValueKind.Present) { @@ -69,14 +70,14 @@ function writeValue(builder: StringBuilder, data: any, key: any, f: Enc.FieldDef } else { const val = f.value(key, data); const t = f.type; - if (t === Enc.FieldType.Str) { + if (t === CIFField.Type.Str) { if (isMultiline(val as string)) { writeMultiline(builder, val as string); return true; } else { return writeChecked(builder, val as string); } - } else if (t === Enc.FieldType.Int) { + } else if (t === CIFField.Type.Int) { writeInteger(builder, val as number); } else { writeFloat(builder, val as number, floatPrecision); @@ -85,42 +86,42 @@ function writeValue(builder: StringBuilder, data: any, key: any, f: Enc.FieldDef return false; } -function getFloatPrecisions(cat: Enc.CategoryInstance) { +function getFloatPrecisions(cat: CIFCategory) { 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; + for (const f of cat.fields) { + ret[ret.length] = f.type === CIFField.Type.Float ? Math.pow(10, CIFField.getDigitCount(f)) : 0; } return ret; } -function writeCifSingleRecord(category: Enc.CategoryInstance<any>, builder: StringBuilder) { - const fields = category.definition.fields; +function writeCifSingleRecord(category: CIFCategory<any>, builder: StringBuilder) { + const fields = category.fields; const data = category.data; - const width = fields.reduce((w, s) => Math.max(w, s.name.length), 0) + category.definition.name.length + 6; + const width = fields.reduce((w, s) => Math.max(w, s.name.length), 0) + category.name.length + 6; - const it = category.keys(); + const it = category.keys ? category.keys() : Iterator.Range(0, category.rowCount - 1); 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); + StringBuilder.writePadRight(builder, `_${category.name}.${f.name}`, width); const multiline = writeValue(builder, data, key, f, precisions[_f]); if (!multiline) StringBuilder.newline(builder); } StringBuilder.write(builder, '#\n'); } -function writeCifLoop(categories: Enc.CategoryInstance[], builder: StringBuilder) { +function writeCifLoop(categories: CIFCategory[], builder: StringBuilder) { const first = categories[0]; - const fields = first.definition.fields; + const fields = first.fields; const fieldCount = fields.length; const precisions = getFloatPrecisions(first); writeLine(builder, 'loop_'); for (let i = 0; i < fieldCount; i++) { - writeLine(builder, `_${first.definition.name}.${fields[i].name}`); + writeLine(builder, `_${first.name}.${fields[i].name}`); } for (let _c = 0; _c < categories.length; _c++) { @@ -129,7 +130,7 @@ function writeCifLoop(categories: Enc.CategoryInstance[], builder: StringBuilder if (category.rowCount === 0) continue; - const it = category.keys(); + const it = category.keys ? category.keys() : Iterator.Range(0, category.rowCount - 1); while (it.hasNext) { const key = it.move(); diff --git a/src/mol-model/structure/export/mmcif.ts b/src/mol-model/structure/export/mmcif.ts index f5c4193134fba3b1e2ef0b0bfbfce84b0188f921..04eb26fba33fc8231386db164aa997f3f9b0005f 100644 --- a/src/mol-model/structure/export/mmcif.ts +++ b/src/mol-model/structure/export/mmcif.ts @@ -5,9 +5,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Column } from 'mol-data/db' -import Iterator from 'mol-data/iterator' -import * as Encoder from 'mol-io/writer/cif' +import { CIFEncoder, createCIFEncoder, CIFCategory, CIFField } from 'mol-io/writer/cif' // import { mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif' import { Structure, Element } from '../structure' import { Model } from '../model' @@ -18,108 +16,51 @@ interface Context { model: Model } -function str<K, D>(name: string, value: (k: K, d: D) => string, valueKind?: (k: K) => Column.ValueKind): Encoder.FieldDefinition<K, D> { - return { name, type: Encoder.FieldType.Str, value, valueKind } +const atom_site_fields: CIFField<Element.Location>[] = [ + CIFField.str('group_PDB', P.residue.group_PDB), + CIFField.int('id', P.atom.id), + CIFField.str('type_symbol', P.atom.type_symbol as any), + CIFField.str('label_atom_id', P.atom.label_atom_id), + CIFField.str('label_alt_id', P.atom.label_alt_id), + + CIFField.str('label_comp_id', P.residue.label_comp_id), + CIFField.int('label_seq_id', P.residue.label_seq_id), + CIFField.str('pdbx_PDB_ins_code', P.residue.pdbx_PDB_ins_code), + + CIFField.str('label_asym_id', P.chain.label_asym_id), + CIFField.str('label_entity_id', P.chain.label_entity_id), + + CIFField.float('Cartn_x', P.atom.x), + CIFField.float('Cartn_y', P.atom.y), + CIFField.float('Cartn_z', P.atom.z), + CIFField.float('occupancy', P.atom.occupancy), + CIFField.int('pdbx_formal_charge', P.atom.pdbx_formal_charge), + + CIFField.str('auth_atom_id', P.atom.auth_atom_id), + CIFField.str('auth_comp_id', P.residue.auth_comp_id), + CIFField.int('auth_seq_id', P.residue.auth_seq_id), + CIFField.str('auth_asym_id', P.chain.auth_asym_id), + + CIFField.int('pdbx_PDB_model_num', P.unit.model_num), + CIFField.str('operator_name', P.unit.operator_name) +]; + +function entityProvider({ model }: Context): CIFCategory { + return CIFCategory.ofTable('entity', model.entities.data); } -function int<K, D = any>(name: string, value: (k: K, d: D) => number, valueKind?: (k: K) => Column.ValueKind): Encoder.FieldDefinition<K, D> { - return { name, type: Encoder.FieldType.Int, value, valueKind } -} - -function float<K, D = any>(name: string, value: (k: K, d: D) => number, valueKind?: (k: K) => Column.ValueKind): Encoder.FieldDefinition<K, D> { - return { name, type: Encoder.FieldType.Float, value, valueKind, digitCount: 3 } -} - -// function col<K, D>(name: string, c: (data: D) => Column<any>): Encoder.FieldDefinition<K, D> { -// const kind = c['@type'].kind; -// // TODO: matrix/vector/support -// const type = kind === 'str' ? Encoder.FieldType.Str : kind === 'int' ? Encoder.FieldType.Int : Encoder.FieldType.Float -// return { name, type, value, valueKind } -// } - -// type Entity = Table.Columns<typeof mmCIF_Schema.entity> - -// const entity: Encoder.CategoryDefinition<number, Entity> = { -// name: 'entity', -// fields: ofSchema(mmCIF_Schema.entity) -// } - -// [ -// str('id', (i, e) => e.id.value(i)), -// str('type', (i, e) => e.type.value(i)), -// str('src_method', (i, e) => e.src_method.value(i)), -// str('pdbx_description', (i, e) => e.pdbx_description.value(i)), -// int('formula_weight', (i, e) => e.formula_weight.value(i)), -// float('pdbx_number_of_molecules', (i, e) => e.pdbx_number_of_molecules.value(i)), -// str('details', (i, e) => e.details.value(i)), -// str('pdbx_mutation', (i, e) => e.pdbx_mutation.value(i)), -// str('pdbx_fragment', (i, e) => e.pdbx_fragment.value(i)), -// str('pdbx_ec', (i, e) => e.pdbx_ec.value(i)), -// ] - -// type AtomSite = typeof mmCIF_Schema.atom_site; -// type DataSource<Key, Schema extends Table.Schema> = { [P in keyof Schema]: (key: Key) => Schema[P]['T'] } - -// export const atom_site1: Partial<DataSource<Atom.Location, AtomSite>> = { -// group_PDB: P.residue.group_PDB, -// id: P.atom.id, -// type_symbol: P.atom.type_symbol as any, -// label_atom_id: P.atom.label_atom_id, -// //... -// } - -const atom_site: Encoder.CategoryDefinition<Element.Location> = { - name: 'atom_site', - fields: [ - str('group_PDB', P.residue.group_PDB), - int('id', P.atom.id), - str('type_symbol', P.atom.type_symbol as any), - str('label_atom_id', P.atom.label_atom_id), - str('label_alt_id', P.atom.label_alt_id), - - str('label_comp_id', P.residue.label_comp_id), - int('label_seq_id', P.residue.label_seq_id), - str('pdbx_PDB_ins_code', P.residue.pdbx_PDB_ins_code), - - str('label_asym_id', P.chain.label_asym_id), - str('label_entity_id', P.chain.label_entity_id), - - float('Cartn_x', P.atom.x), - float('Cartn_y', P.atom.y), - float('Cartn_z', P.atom.z), - float('occupancy', P.atom.occupancy), - int('pdbx_formal_charge', P.atom.pdbx_formal_charge), - - str('auth_atom_id', P.atom.auth_atom_id), - str('auth_comp_id', P.residue.auth_comp_id), - int('auth_seq_id', P.residue.auth_seq_id), - str('auth_asym_id', P.chain.auth_asym_id), - - int('pdbx_PDB_model_num', P.unit.model_num), - str('operator_name', P.unit.operator_name) - ] -}; - -function entityProvider({ model }: Context): Encoder.CategoryInstance { - return { - data: model.entities.data, - definition: Encoder.CategoryDefinition.ofTable('entity', model.entities.data), - keys: () => Iterator.Range(0, model.entities.data._rowCount - 1), - rowCount: model.entities.data._rowCount - } -} - -function atomSiteProvider({ structure }: Context): Encoder.CategoryInstance { +function atomSiteProvider({ structure }: Context): CIFCategory { return { data: void 0, - definition: atom_site, - keys: () => structure.elementLocations(), - rowCount: structure.elementCount + name: 'atom_site', + fields: atom_site_fields, + rowCount: structure.elementCount, + keys: () => structure.elementLocations() } } /** Doesn't start a data block */ -export function encode_mmCIF_categories(encoder: Encoder.EncoderInstance, structure: Structure) { +export function encode_mmCIF_categories(encoder: CIFEncoder, structure: Structure) { const models = Structure.getModels(structure); if (models.length !== 1) throw 'Can\'t export stucture composed from multiple models.'; const model = models[0]; @@ -130,7 +71,7 @@ export function encode_mmCIF_categories(encoder: Encoder.EncoderInstance, struct } function to_mmCIF(name: string, structure: Structure, asBinary = false) { - const w = Encoder.create({ binary: asBinary }); + const w = createCIFEncoder({ binary: asBinary }); w.startDataBlock(name); encode_mmCIF_categories(w, structure); return w.getData(); diff --git a/src/perf-tests/cif-encoder.ts b/src/perf-tests/cif-encoder.ts index 5f22508252cf5991273ced66326491b48f587b86..4e45528dc6c97892b34104a7276b163db4629535 100644 --- a/src/perf-tests/cif-encoder.ts +++ b/src/perf-tests/cif-encoder.ts @@ -4,55 +4,32 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import Iterator from 'mol-data/iterator' -import * as Enc from 'mol-io/writer/cif' + import { CIFCategory, CIFField, createCIFEncoder } from 'mol-io/writer/cif' -const category1: Enc.CategoryDefinition<number> = { - name: 'test', - fields: [{ - name: 'f1', - type: Enc.FieldType.Str, - value: i => 'v' + i - }, { - name: 'f2', - type: Enc.FieldType.Int, - value: i => i * i - }, { - name: 'f3', - type: Enc.FieldType.Float, - value: i => Math.random() - }] -} +const category1fields: CIFField[] = [ + CIFField.str('f1', i => 'v' + i), + CIFField.int('f2', i => i * i), + CIFField.float('f3', i => Math.random()), +]; -const category2: Enc.CategoryDefinition<number> = { - name: 'test2', - fields: [{ - name: 'e1', - type: Enc.FieldType.Str, - value: i => 'v\n' + i - }, { - name: 'e2', - type: Enc.FieldType.Int, - value: i => i * i - }, { - name: 'e3', - type: Enc.FieldType.Float, - value: i => Math.random() - }] -} +const category2fields: CIFField[] = [ + CIFField.str('e1', i => 'v\n' + i), + CIFField.int('e2', i => i * i), + CIFField.float('e3', i => Math.random()), +]; -function getInstance(ctx: { cat: Enc.CategoryDefinition<number>, rowCount: number }): Enc.CategoryInstance { +function getInstance(ctx: { name: string, fields: CIFField[], rowCount: number }): CIFCategory { return { data: void 0, - definition: ctx.cat, - keys: () => Iterator.Range(0, ctx.rowCount - 1), + name: ctx.name, + fields: ctx.fields, rowCount: ctx.rowCount } } -const w = Enc.create(); +const w = createCIFEncoder(); w.startDataBlock('test'); -w.writeCategory(getInstance, [{ rowCount: 5, cat: category1 }]); -w.writeCategory(getInstance, [{ rowCount: 1, cat: category2 }]); +w.writeCategory(getInstance, [{ rowCount: 5, name: 'cat1', fields: category1fields }]); +w.writeCategory(getInstance, [{ rowCount: 1, name: 'cat2', fields: category2fields }]); console.log(w.getData()); diff --git a/src/servers/model/server/query.ts b/src/servers/model/server/query.ts index 9f0e9e68ccc9efee59669acfe1db8eaa31257cdd..3839a430d15aaaaba30d2462dccf71f9f7ed1c6a 100644 --- a/src/servers/model/server/query.ts +++ b/src/servers/model/server/query.ts @@ -11,12 +11,11 @@ import Config from '../config'; import { Progress, now } from 'mol-task'; import { ConsoleLogger } from 'mol-util/console-logger'; import Writer from 'mol-io/writer/writer'; -import * as Encoder from 'mol-io/writer/cif' +import { CIFCategory, CIFField, createCIFEncoder } from 'mol-io/writer/cif' import { encode_mmCIF_categories } from 'mol-model/structure/export/mmcif'; import { Selection } from 'mol-model/structure'; import Version from '../version' import { Column } from 'mol-data/db'; -import { Iterator } from 'mol-data'; import { PerformanceMonitor } from 'mol-util/performance-monitor'; export interface ResponseFormat { @@ -75,7 +74,7 @@ export async function resolveRequest(req: Request, writer: Writer) { ConsoleLogger.logId(req.id, 'Query', 'Query finished.'); - const encoder = Encoder.create({ binary: req.responseFormat.isBinary, encoderName: `ModelServer ${Version}` }); + const encoder = createCIFEncoder({ binary: req.responseFormat.isBinary, encoderName: `ModelServer ${Version}` }); perf.start('encode'); encoder.startDataBlock('result'); @@ -106,21 +105,18 @@ export function abortingObserver(p: Progress) { } } -type FieldDesc<T> = Encoder.FieldDefinition<number, T> -type CategoryInstance = Encoder.CategoryInstance - -function string<T>(name: string, str: (data: T, i: number) => string, isSpecified?: (data: T) => boolean): FieldDesc<T> { +function string<T>(name: string, str: (data: T, i: number) => string, isSpecified?: (data: T) => boolean): CIFField<number, T> { if (isSpecified) { - return { name, type: Encoder.FieldType.Str, value: (i, d) => str(d, i), valueKind: (i, d) => isSpecified(d) ? Column.ValueKind.Present : Column.ValueKind.NotPresent }; + return CIFField.str(name, (i, d) => str(d, i), { valueKind: (i, d) => isSpecified(d) ? Column.ValueKind.Present : Column.ValueKind.NotPresent }); } - return { name, type: Encoder.FieldType.Str, value: (i, d) => str(d, i) }; + return CIFField.str(name, (i, d) => str(d, i)); } -function int32<T>(name: string, value: (data: T) => number): FieldDesc<T> { - return { name, type: Encoder.FieldType.Int, value: (i, d) => value(d) }; +function int32<T>(name: string, value: (data: T) => number): CIFField<number, T> { + return CIFField.int(name, (i, d) => value(d)); } -const _model_server_result_fields: FieldDesc<Request>[] = [ +const _model_server_result_fields: CIFField<number, Request>[] = [ string<Request>('request_id', ctx => '' + ctx.id), string<Request>('datetime_utc', ctx => ctx.datetime_utc), string<Request>('server_version', ctx => Version), @@ -129,12 +125,12 @@ const _model_server_result_fields: FieldDesc<Request>[] = [ string<Request>('entry_id', ctx => ctx.entryId), ]; -const _model_server_params_fields: FieldDesc<string[]>[] = [ +const _model_server_params_fields: CIFField<number, string[]>[] = [ string<string[]>('name', (ctx, i) => ctx[i][0]), string<string[]>('value', (ctx, i) => ctx[i][1]) ]; -const _model_server_stats_fields: FieldDesc<Stats>[] = [ +const _model_server_stats_fields: CIFField<number, Stats>[] = [ int32<Stats>('io_time_ms', ctx => ctx.structure.info.readTime | 0), int32<Stats>('parse_time_ms', ctx => ctx.structure.info.parseTime | 0), int32<Stats>('create_model_time_ms', ctx => ctx.structure.info.createModelTime | 0), @@ -143,33 +139,33 @@ const _model_server_stats_fields: FieldDesc<Stats>[] = [ ]; -function _model_server_result(request: Request): CategoryInstance { +function _model_server_result(request: Request): CIFCategory { return { data: request, - definition: { name: 'model_server_result', fields: _model_server_result_fields }, - keys: () => Iterator.Value(0), + name: 'model_server_result', + fields: _model_server_result_fields, rowCount: 1 }; } -function _model_server_params(request: Request): CategoryInstance { +function _model_server_params(request: Request): CIFCategory { const params: string[][] = []; for (const k of Object.keys(request.normalizedParams)) { params.push([k, '' + request.normalizedParams[k]]); } return { data: params, - definition: { name: 'model_server_params', fields: _model_server_params_fields }, - keys: () => Iterator.Range(0, params.length - 1), + name: 'model_server_params', + fields: _model_server_params_fields, rowCount: params.length }; } -function _model_server_stats(stats: Stats): CategoryInstance { +function _model_server_stats(stats: Stats): CIFCategory { return { data: stats, - definition: { name: 'model_server_stats', fields: _model_server_stats_fields }, - keys: () => Iterator.Value(0), + name: 'model_server_stats', + fields: _model_server_stats_fields, rowCount: 1 }; } \ No newline at end of file diff --git a/src/servers/volume/server/query/encode.ts b/src/servers/volume/server/query/encode.ts index 525e365ea1de97ed074abf06249a0d897c52628a..a63af26ceb1da1915388152d7bb90dad7d1b66d5 100644 --- a/src/servers/volume/server/query/encode.ts +++ b/src/servers/volume/server/query/encode.ts @@ -6,17 +6,16 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import * as Encoder from 'mol-io/writer/cif' +import { CIFEncoder, CIFCategory, CIFField, createCIFEncoder } from 'mol-io/writer/cif' import * as Data from './data-model' import * as Coords from '../algebra/coordinate' 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'; export default function encode(query: Data.QueryContext, output: Data.QueryOutputStream) { - let w = Encoder.create({ binary: query.params.asBinary, encoderName: `VolumeServer ${VERSION}` }); + let w = createCIFEncoder({ binary: query.params.asBinary, encoderName: `VolumeServer ${VERSION}` }); write(w, query); w.encode(); w.writeTo(output); @@ -27,26 +26,21 @@ interface ResultContext { channelIndex: number } -//type Writer = CIF.Writer<ResultContext | Data.QueryContext> +type FieldDesc<T> = CIFField<number, T> -type FieldDesc<T> = Encoder.FieldDefinition<number, T> -type CategoryInstance = Encoder.CategoryInstance - -//import E = CIF.Binary.Encoder - -function string<T>(name: string, str: (data: T) => string, isSpecified?: (data: T) => boolean): FieldDesc<T> { +function string<T>(name: string, str: (data: T) => string, isSpecified?: (data: T) => boolean): CIFField<number, T> { if (isSpecified) { - return { name, type: Encoder.FieldType.Str, value: (i, d) => str(d), valueKind: (i, d) => isSpecified(d) ? Column.ValueKind.Present : Column.ValueKind.NotPresent }; + return CIFField.str(name, (i, d) => str(d), { valueKind: (i, d) => isSpecified(d) ? Column.ValueKind.Present : Column.ValueKind.NotPresent }); } - return { name, type: Encoder.FieldType.Str, value: (i, d) => str(d) }; + return CIFField.str(name, (i, d) => str(d)); } -function int32<T>(name: string, value: (data: T) => number): FieldDesc<T> { - return { name, type: Encoder.FieldType.Int, value: (i, d) => value(d) }; +function int32<T>(name: string, value: (data: T) => number): CIFField<number, T> { + return CIFField.int(name, (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 }; + return CIFField.float(name, (i, d) => value(d), { digitCount: digitCount, typedArray: Float64Array }); } interface _vd3d_Ctx { @@ -98,7 +92,7 @@ const _volume_data_3d_info_fields: FieldDesc<_vd3d_Ctx>[] = [ float64<_vd3d_Ctx>('max_sampled', ctx => ctx.sampledValuesInfo.max) ]; -function _volume_data_3d_info(result: ResultContext): CategoryInstance { +function _volume_data_3d_info(result: ResultContext): CIFCategory { const ctx: _vd3d_Ctx = { header: result.query.data.header, channelIndex: result.channelIndex, @@ -110,8 +104,8 @@ function _volume_data_3d_info(result: ResultContext): CategoryInstance { return { data: ctx, - definition: { name: 'volume_data_3d_info', fields: _volume_data_3d_info_fields }, - keys: () => Iterator.Value(0), + name: 'volume_data_3d_info', + fields: _volume_data_3d_info_fields, rowCount: 1 }; } @@ -143,14 +137,9 @@ function _volume_data_3d(ctx: ResultContext) { 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 }]; + const fields: FieldDesc<typeof data>[] = [CIFField.float('values', _volume_data_3d_number, { encoder, typedArray, digitCount: 6 })] - return { - data, - definition: { name: 'volume_data_3d', fields }, - keys: () => Iterator.Range(0, data.length - 1), - rowCount: data.length - }; + return { data, name: 'volume_data_3d', fields, rowCount: data.length }; } function pickQueryBoxDimension(ctx: Data.QueryContext, e: 'a' | 'b', d: number) { @@ -185,16 +174,16 @@ const _density_server_result_fields: FieldDesc<Data.QueryContext>[] = [ queryBoxDimension('b', 2) ] -function _density_server_result(ctx: Data.QueryContext) { +function _density_server_result(ctx: Data.QueryContext): CIFCategory { return { data: ctx, - definition: { name: 'density_server_result', fields: _density_server_result_fields }, - keys: () => Iterator.Value(0), + name: 'density_server_result', + fields: _density_server_result_fields, rowCount: 1 }; } -function write(encoder: Encoder.EncoderInstance, query: Data.QueryContext) { +function write(encoder: CIFEncoder, query: Data.QueryContext) { encoder.startDataBlock('SERVER'); encoder.writeCategory(_density_server_result, [query]);