From 4492bd6134544a1b7240d40b511486fd178f381c Mon Sep 17 00:00:00 2001
From: David Sehnal <david.sehnal@gmail.com>
Date: Fri, 6 Oct 2017 14:04:02 +0200
Subject: [PATCH] Working on structure data model

---
 src/structure/data.ts             |  14 ++++
 src/structure/model.ts            | 130 ++++++++++++++++++++----------
 src/structure/selectors.ts        |  11 +++
 src/structure/selectors/common.ts |  47 +++++++++++
 src/structure/selectors/mmcif.ts  |  34 ++++++++
 src/utils/linear-algebra.ts       |   3 +-
 6 files changed, 194 insertions(+), 45 deletions(-)
 create mode 100644 src/structure/selectors.ts
 create mode 100644 src/structure/selectors/common.ts
 create mode 100644 src/structure/selectors/mmcif.ts

diff --git a/src/structure/data.ts b/src/structure/data.ts
index 09332de8d..d162fc1b3 100644
--- a/src/structure/data.ts
+++ b/src/structure/data.ts
@@ -32,6 +32,20 @@ export interface Chains extends DataTable<Chain> { }
 export interface Entity { key: number, id: string }
 export interface Entities extends DataTable<Entity> { }
 
+export interface Bonds extends Readonly<{
+    /**
+     * Where bonds for atom A start and end.
+     * Start at idx, end at idx + 1
+     */
+    offset: ArrayLike<number>,
+    neighbor: ArrayLike<number>,
+
+    order: ArrayLike<number>,
+    flags: ArrayLike<number>,
+
+    count: number
+}> { }
+
 export type SourceData =
     | { kind: 'mmCIF', data: any  } // TODO
     | { kind: 'custom', data: any  } // TODO
diff --git a/src/structure/model.ts b/src/structure/model.ts
index 47b1e863e..2ec048765 100644
--- a/src/structure/model.ts
+++ b/src/structure/model.ts
@@ -5,22 +5,32 @@
  */
 
 import * as Data from './data'
+import { Selectors } from './selectors'
 import { Vec3, Mat4 } from '../utils/linear-algebra'
 
+let _uid = 0;
+/** Model-related unique identifiers */
+export function getUid() { return _uid++; }
 
 /** Unit = essentially a list of residues (usually a chain) */
 export interface Unit extends Readonly<{
+    operator: Unit.Operator,
     /** The static part (essentially residue annotations) */
-    structre: Unit.Structure,
+    structure: Unit.Structure,
     /** 3D arrangement that often changes with time. */
-    conformation: Unit.Conformation
+    conformation: Unit.Conformation,
+    /** Position of i-th atom. Special function for this because it's the only one that depends on "operator" */
+    atomPosition(i: number, p: Vec3): Vec3,
+    /** Property access */
+    selectors: Selectors
 }> { }
 
 export namespace Unit {
     export interface Structure extends Readonly<{
+        /** A globally unique identifier for this instance (to easily determine unique structures within a model) */
+        id: number,
+        /** Source data for this structure */
         data: Data.Structure,
-        /** A globally unique number for this instance (to easily determine unique structures within a model) */
-        key: number,
         /** Reference to the data.entities table */
         entity: number,
         /** Reference to the data.chains table */
@@ -29,32 +39,40 @@ export namespace Unit {
         residues: ArrayLike<number>,
         /** Offsets of atoms in the residue layer. start = offsets[i], endExclusive = offsets[i + 1] */
         atomOffsets: ArrayLike<number>,
+        /** Indices into data.atoms table */
+        atoms: ArrayLike<number>
         /** Index of a residue in the corresponding residues array. */
-        atomResidue: number
+        atomResidue: ArrayLike<number>
     }> { }
 
-    export interface Bonds extends Readonly<{
-        /**
-         * Where bonds for atom A start and end.
-         * Start at idx, end at idx + 1
-         */
-        offset: ArrayLike<number>,
-        neighbor: ArrayLike<number>,
+    export interface Conformation extends Readonly<{
+        /** A globally unique identifier for this instance (to easily determine unique structures within a model) */
+        id: number,
 
-        order: ArrayLike<number>,
-        flags: ArrayLike<number>,
+        positions: Data.Positions,
 
-        count: number
-    }> { }
+        /** Per residue secondary structure assignment. */
+        secondaryStructure: Data.SecondaryStructures,
 
-    export interface Conformation extends Readonly<{
-        positions: Data.Positions
-        spatialLookup: any, // TODO
-        boundingSphere: { readonly center: Vec3, readonly radius: number },
-        secodaryStructure: Data.SecondaryStructures,
-        bonds: Bonds
+        '@spatialLookup': any, // TODO
+        '@boundingSphere': Readonly<{ center: Vec3, radius: number }>,
+        '@bonds': Data.Bonds
     }> { }
 
+    export namespace Conformation {
+        export function spatialLookup(conformation: Conformation): any {
+            throw 'not implemented'
+        }
+
+        export function boundingSphere(conformation: Conformation): any {
+            throw 'not implemented'
+        }
+
+        export function bonds(conformation: Conformation): any {
+            throw 'not implemented'
+        }
+    }
+
     export type OperatorKind =
         | { kind: 'identity' }
         | { kind: 'symmetry', id: string, hkl: Vec3 }
@@ -66,31 +84,38 @@ export namespace Unit {
         inverse: Mat4
     }> { }
 
-    export interface Lookup3D {
+    export interface SpatialLookup {
         // TODO
     }
 }
 
 export interface Model extends Readonly<{
-    index: number,
+    units: Unit[],
+
+    structure: { [id: number]: Unit.Structure },
+    conformation: { [id: number]: Unit.Conformation },
 
-    structure: Model.Structure,
-    conformation: Model.Conformation
+    '@unitLookup'?: Unit.SpatialLookup,
+    /** bonds between units */
+    '@unitBonds'?: Data.Bonds
 }> { }
 
 export namespace Model {
-    // TODO: model residues between units
-    // use a map of map of bonds?
+    export function unitLookup(model: Model): Unit.SpatialLookup {
+        throw 'not implemented';
+    }
 
-    export interface Structure extends Readonly<{
-        operators: Unit.Operator[],
-        units: Unit.Structure[]
-    }> { }
+    export function unitBonds(model: Model): Data.Bonds {
+        throw 'not implemented';
+    }
 
-    export interface Conformation extends Readonly<{
-        units: Unit.Conformation[],
-        spatialLookup: Unit.Lookup3D
-    }> { }
+    export function join(a: Model, b: Model): Model {
+        return {
+            units: [...a.units, ...b.units],
+            structure: { ...a.structure, ...b.structure },
+            conformation: { ...a.conformation, ...b.conformation }
+        }
+    }
 }
 
 export namespace Atom {
@@ -100,30 +125,49 @@ export namespace Atom {
      * a both number[] and Float64Array(), making it much
      * more efficient than storing an array of objects.
      */
-    export type Reference = number
-    export interface Location { unit: number, atom: number }
+    export type PackedReference = number
+    export interface Reference { unit: number, atom: number }
+    export type Selection = ArrayLike<PackedReference>
 
     const { _uint32, _float64 } = (function() {
         const data = new ArrayBuffer(8);
         return { _uint32: new Uint32Array(data), _float64: new Float64Array(data) };
     }());
 
-    export function emptyLocation(): Location { return { unit: 0, atom: 0 }; }
+    export function emptyLocation(): Reference { return { unit: 0, atom: 0 }; }
 
-    export function getRef(location: Location) {
+    export function packRef(location: Reference) {
         _uint32[0] = location.unit;
         _uint32[1] = location.atom;
         return _float64[0];
     }
 
-    export function getLocation(ref: Reference) {
-        return updateLocation(ref, emptyLocation());
+    export function unpackRef(ref: PackedReference) {
+        return updateRef(ref, emptyLocation());
     }
 
-    export function updateLocation(ref: Reference, location: Location): Location {
+    export function updateRef(ref: PackedReference, location: Reference): Reference {
         _float64[0] = ref;
         location.unit = _uint32[0];
         location.atom = _uint32[1];
         return location;
     }
 }
+
+export interface Selector<T> {
+    '@type': T,
+    create(m: Model, u: number): (a: number, v?: T) => T
+}
+
+export function Selector<T>(create: (m: Model, u: number) => (a: number, v?: T) => T): Selector<T> {
+    return { create } as Selector<T>;
+}
+
+export namespace Selector {
+    export type Field<T> = (a: number, v?: T) => T;
+
+    export type Category = { [s: string]: Selector<any> };
+    export type Unit<C extends Category> = { [F in keyof C]: Field<C[F]['@type']> }
+
+    export type Set<S extends { [n: string]: Category }> = { [C in keyof S]: Unit<S[C]> }
+}
\ No newline at end of file
diff --git a/src/structure/selectors.ts b/src/structure/selectors.ts
new file mode 100644
index 000000000..c8e599b3a
--- /dev/null
+++ b/src/structure/selectors.ts
@@ -0,0 +1,11 @@
+/**
+ * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { Selector } from './model'
+
+const set = {};
+
+export type Selectors = Selector.Set<typeof set>
\ No newline at end of file
diff --git a/src/structure/selectors/common.ts b/src/structure/selectors/common.ts
new file mode 100644
index 000000000..76b4c876f
--- /dev/null
+++ b/src/structure/selectors/common.ts
@@ -0,0 +1,47 @@
+// /**
+//  * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
+//  *
+//  * @author David Sehnal <david.sehnal@gmail.com>
+//  */
+
+// import { Selector } from '../model'
+// //import * as Data from '../data'
+// //import { Vec3 } from '../../utils/linear-algebra'
+
+// export const atom = {
+//     name: Selector((m, u) => {
+//         const unit = m.units[u].structure;
+//         const atoms = unit.atoms;
+//         const name = unit.data.atoms.name;
+//         return a => name[atoms[a]];
+//     }),
+//     // position: Selector<Vec3>((m, u) => {
+//     //     const unit = m.units[u].structure;
+//     //     const atoms = unit.atoms;
+//     //     const { positions: { x, y, z } } = m.conformation[u];
+//     //     const { operator: { transform } } = unit;
+//     //     return (atom, position) => {
+//     //         const a = atoms[atom];
+//     //         const p = position || Vec3.zero();
+//     //         Vec3.set(p, x[a], y[a], z[a]);
+//     //         return Vec3.transformMat4(p, p, transform);
+//     //     };
+//     // }),
+//     // inversePosition: Selector<Vec3>((m, u) => {
+//     //     const unit = m.structure[u];
+//     //     const atoms = unit.atoms;
+//     //     const { positions: { x, y, z } } = m.conformation[u];
+//     //     const { operator: { inverse } } = unit;
+
+//     //     return (atom, position) => {
+//     //         const a = atoms[atom];
+//     //         const p = position || Vec3.zero();
+//     //         Vec3.set(p, x[a], y[a], z[a]);
+//     //         return Vec3.transformMat4(p, p, inverse);
+//     //     };
+//     // })
+// }
+
+// export const residue = {
+
+// }
\ No newline at end of file
diff --git a/src/structure/selectors/mmcif.ts b/src/structure/selectors/mmcif.ts
new file mode 100644
index 000000000..08a95c232
--- /dev/null
+++ b/src/structure/selectors/mmcif.ts
@@ -0,0 +1,34 @@
+// /**
+//  * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
+//  *
+//  * @author David Sehnal <david.sehnal@gmail.com>
+//  */
+
+// import { Model, Unit, PropertyGetterProvider } from '../model'
+// import * as Data from '../data'
+// //import { Vec3 } from '../../utils/linear-algebra'
+
+// // function provider<T>(p: (model: Model) => Property<T>): PropertyProvider<T> {
+// //     return p;
+// // }
+
+// function atomPropertyProvider<T>(property: (ps: Model['structure']['properties']) => ArrayLike<T>): PropertyGetterProvider<T> {
+//     return m => {
+//         const a = m.structure.properties.atoms;
+//         const p = property(m.structure.properties);
+//         return ({ unit, atom }) => p[a[unit][atom]];
+//     };
+// }
+
+// function unitStructureProvider<T>(p: (structure: Unit.Structure, data: Data.Structure, atom: number) => T): PropertyGetterProvider<T> {
+//     return m => {
+//         const units = m.structure.units;
+//         const data = m.structure.properties.data;
+//         return ({ unit, atom }) => p(units[unit], data[unit], atom);
+//     };
+// }
+
+// export const atom_site = {
+//     label_atom_id: atomPropertyProvider(ps => ps.),
+//     label_comp_id: unitStructureProvider((u, d, a) => d.residues.name[u.atomResidue[a]])
+// }
\ No newline at end of file
diff --git a/src/utils/linear-algebra.ts b/src/utils/linear-algebra.ts
index fa6af909b..10f1ea666 100644
--- a/src/utils/linear-algebra.ts
+++ b/src/utils/linear-algebra.ts
@@ -572,8 +572,7 @@ export namespace Vec3 {
 
     export function transformMat4(out: number[], a: number[], m: number[]) {
         let x = a[0], y = a[1], z = a[2],
-            w = m[3] * x + m[7] * y + m[11] * z + m[15];
-        w = w || 1.0;
+            w = (m[3] * x + m[7] * y + m[11] * z + m[15]) || 1.0;
         out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w;
         out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w;
         out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w;
-- 
GitLab