From 37a0b07d56be0a011f533078f77f77d9c588c563 Mon Sep 17 00:00:00 2001
From: Sebastian Bittrich <bittrich@hs-mittweida.de>
Date: Tue, 4 Jun 2019 17:52:02 -0700
Subject: [PATCH] stub for encoding config

---
 package-lock.json                       | Bin 575779 -> 576329 bytes
 src/mol-io/writer/cif.ts                |  53 ++++++++++++++
 src/mol-io/writer/cif/encoder/binary.ts |   7 +-
 src/tests/browser/encoding-config.ts    |  91 ++++++++++++++++++++++++
 webpack.config.js                       |   1 +
 5 files changed, 149 insertions(+), 3 deletions(-)
 create mode 100644 src/tests/browser/encoding-config.ts

diff --git a/package-lock.json b/package-lock.json
index 6c89b0f359c83badbc08f0ea4223fd151c4475cc..e79e6e264ae78f3a1bc547133c07fb54afdc54c2 100644
GIT binary patch
delta 512
zcmYk2yDvj=6vy+s=l*Vc`@B-LR1x)_JO&-AZ48|%`WMI@jE&SJji^Q>H4x>}6A2Bm
zl8TVuU_c_l)CIBWCaFg!Ta{Z9_YP+`-^b_oIN0#sWxc5-a>_+27;OU&%B|qU*Ly)^
zu?jZ?Ix+OdQ?xdL6>q+|XOt+|F<#~Ra5zCamEr_FE5QrV3R@*Ly8HodlmKQm9diJO
zy45&M>@3d1x}=HB)d1H-cSb4#ezdF-6B~8HvEGpx{9fx(Yb#L)kMadIVC<6<(bfzP
ze<(QszU&-FF*ybuSU#twY?-vw;%w3lUwUl&AXqSXOKmLUfq+?qV<jO-Sx`Ty+6z;<
z3Y+hToJALmXE+|W>%ffilOQT<-4cev#EwThytD~DY;OeWsrE)z@WVc*?N}Hmlx2co
z7Ie_aA~oUrF7c@ZT*#4`f6uHGgRXQw4C?UeP?ir1Cs#PILK>C4BU!Vt&}E=$hiG69
I(I{R107QbHtN;K2

delta 371
zcmXAjKS%;$7{>YT{q8QjE(H-aFFg|bBasmfMT6E54i2{lAv6UA88n9E5J+2e<qLs-
zAle*6s&8;ep{SN{awtflp&`nqXs}59h6f&apWpL74_W`&kw3jpE`)d&XLP~I(p_Nz
z(D<YpepE#j>4EG$@j)H8FJ+Cn30Q@G5LW>T+=VEVIBXT6Xyf+P0XM|d$2_d%gm2Qv
zacCHxP(P<`^m^b8je9wm-4#Tvt-&a6Dv~AJA)0B}Da&c_OR#Z8NAU4hvalB~#1-D0
z2ttZC+ovEu$Tw^s3KVlHRdC~nxG}m+tQbi^Z;S=T(fKT`u+ltKDHeTl1^-SZ6{ATq
z$ItB~S;zDQ7+820rqR|Rs@d2HnB^@WHYOo}U%H&b)-2fA-6GUQ{xWebE^&S@PmVLq
h81zJY5?M1QRqAFf<TA_ll-Z8BGGRq#VP(Kk`v=){ehUBq

diff --git a/src/mol-io/writer/cif.ts b/src/mol-io/writer/cif.ts
index c191d8a4b..762f1988f 100644
--- a/src/mol-io/writer/cif.ts
+++ b/src/mol-io/writer/cif.ts
@@ -53,5 +53,58 @@ export namespace CifWriter {
                 return ff && ff.binaryEncoding ? ArrayEncoder.fromEncoding(ff.binaryEncoding) : void 0;
             }
         }
+    };
+
+    export function createEncodingProviderFromJsonConfig(hints: EncodingStrategyHint[]): EncodingProvider {
+        return {
+            get(c, f) {
+                for (let i = 0; i < hints.length; i++) {
+                    const hint = hints[i];
+                    if (hint.categoryName === c && hint.columnName === f) {
+                        return resolveEncoding(hint);
+                    }
+                }
+            }
+        }
     }
+
+    function resolveEncoding(hint: EncodingStrategyHint): ArrayEncoder | undefined {
+        const precision: number | undefined = hint.precision;
+        if (precision !== void 0) {
+            const multiplier = Math.pow(10, precision);
+            const fixedPoint = E.by(E.fixedPoint(multiplier));
+            switch (hint.encoding) {
+                case 'pack':
+                    return fixedPoint.and(E.integerPacking);
+                case 'rle':
+                    return fixedPoint.and(E.runLength).and(E.integerPacking);
+                case 'delta':
+                    return fixedPoint.and(E.delta).and(E.integerPacking);
+                case 'delta-rle':
+                    return fixedPoint.and(E.delta).and(E.runLength).and(E.integerPacking);
+            };
+        } else {
+            switch (hint.encoding) {
+                case 'pack':
+                    return E.by(E.integerPacking);
+                case 'rle':
+                    return E.by(E.runLength).and(E.integerPacking);
+                case 'delta':
+                    return E.by(E.delta).and(E.integerPacking);
+                case 'delta-rle':
+                    return E.by(E.delta).and(E.runLength).and(E.integerPacking);
+            }
+        }
+    }
+}
+
+// defines the information needed to encode certain fields: category and column name as well as encoding tag, precision is optional and identifies float columns
+// TODO would be nice to infer strategy and precision if needed
+export interface EncodingStrategyHint {
+    categoryName: string,
+    columnName: string,
+    // 'pack', 'rle', 'delta', or 'delta-rle'
+    encoding: string,
+    // number of decimal places to keep - must be specified to float columns
+    precision?: number
 }
\ 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 9212bcea2..99ee50053 100644
--- a/src/mol-io/writer/cif/encoder/binary.ts
+++ b/src/mol-io/writer/cif/encoder/binary.ts
@@ -111,12 +111,13 @@ function getDefaultEncoder(type: Field.Type): ArrayEncoder {
 }
 
 function tryGetEncoder(categoryName: string, field: Field, format: Field.Format | undefined, provider: EncodingProvider | undefined) {
-    if (format && format.encoder) {
+    // TODO made provider the first check - might break servers/model/query
+    if (provider && provider.get(categoryName, field.name)) {
+        return provider.get(categoryName, field.name);
+    } else if (format && format.encoder) {
         return format.encoder;
     } else if (field.defaultFormat && field.defaultFormat.encoder) {
         return field.defaultFormat.encoder;
-    } else if (provider) {
-        return provider.get(categoryName, field.name);
     } else {
         return void 0;
     }
diff --git a/src/tests/browser/encoding-config.ts b/src/tests/browser/encoding-config.ts
new file mode 100644
index 000000000..b02ed4ea8
--- /dev/null
+++ b/src/tests/browser/encoding-config.ts
@@ -0,0 +1,91 @@
+import './index.html'
+import { CIF, CifCategory, CifField, getCifFieldType } from '../../mol-io/reader/cif';
+import { CifWriter } from '../../mol-io/writer/cif';
+import { classifyFloatArray, classifyIntArray } from '../../mol-io/common/binary-cif';
+
+async function parseCif(data: string|Uint8Array) {
+    const comp = CIF.parse(data);
+    const parsed = await comp.run();
+    if (parsed.isError) throw parsed;
+    return parsed.result;
+}
+
+async function downloadCif(url: string, isBinary: boolean) {
+    const data = await fetch(url);
+    return parseCif(isBinary ? new Uint8Array(await data.arrayBuffer()) : await data.text());
+}
+
+async function downloadFromPdb(pdb: string) {
+    const parsed = await downloadCif(`https://webchem.ncbr.muni.cz/ModelServer/static/bcif/${pdb}`, true);
+    return parsed.blocks[0];
+}
+
+async function init(props = {}) {
+    const cif = await downloadFromPdb('1brr')
+    const encoder = CifWriter.createEncoder({
+        binary: true,
+        encoderName: 'mol*',
+        binaryEncodingPovider: CifWriter.createEncodingProviderFromJsonConfig([
+            {
+                'categoryName': 'atom_site',
+                'columnName': 'Cartn_y',
+                'encoding': 'rle',
+                'precision': 0
+            },
+            {
+                'categoryName': 'atom_site',
+                'columnName': 'Cartn_z',
+                'encoding': 'delta',
+                'precision': 1
+            },
+            {
+                'categoryName': 'atom_site',
+                'columnName': 'label_seq_id',
+                'encoding': 'delta-rle'
+            }
+        ])
+    });
+
+    encoder.startDataBlock(cif.header);
+    for (const c of cif.categoryNames) {
+        const cat = cif.categories[c];
+        const fields: CifWriter.Field[] = [];
+        for (const f of cat.fieldNames) {
+            fields.push(classify(f, cat.getField(f)!))
+        }
+
+        encoder.writeCategory(getCategoryInstanceProvider(cif.categories[c], fields));
+    }
+    const ret = encoder.getData() as Uint8Array;
+
+    const cif2 = (await parseCif(ret)).blocks[0];
+    // should be untouched
+    console.log(cif2.categories['atom_site'].getField('Cartn_x'));
+    // should have integer precision
+    console.log(cif2.categories['atom_site'].getField('Cartn_y'));
+    // should have 1 decimal place
+    console.log(cif2.categories['atom_site'].getField('Cartn_z'));
+    console.log(cif2.categories['atom_site'].getField('label_seq_id'));
+}
+
+init()
+
+function getCategoryInstanceProvider(cat: CifCategory, fields: CifWriter.Field[]): CifWriter.Category {
+    return {
+        name: cat.name,
+        instance: () => CifWriter.categoryInstance(fields, { data: cat, rowCount: cat.rowCount })
+    };
+}
+
+function classify(name: string, field: CifField): CifWriter.Field {
+    const type = getCifFieldType(field);
+    if (type['@type'] === 'str') {
+        return { name, type: CifWriter.Field.Type.Str, value: field.str, valueKind: field.valueKind };
+    } else if (type['@type'] === 'float') {
+        const encoder = classifyFloatArray(field.toFloatArray({ array: Float64Array }));
+        return CifWriter.Field.float(name, field.float, { valueKind: field.valueKind, encoder, typedArray: Float64Array });
+    } else {
+        const encoder = classifyIntArray(field.toIntArray({ array: Int32Array }));
+        return CifWriter.Field.int(name, field.int, { valueKind: field.valueKind, encoder, typedArray: Int32Array });
+    }
+}
\ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
index 3de9e1565..c171386df 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -98,4 +98,5 @@ module.exports = [
     createBrowserTest('render-spheres'),
     createBrowserTest('render-structure'),
     createBrowserTest('render-text'),
+    createBrowserTest('encoding-config')
 ]
\ No newline at end of file
-- 
GitLab