diff --git a/src/mol-base/_spec/collections.spec.ts b/src/mol-base/_spec/collections.spec.ts index 07f31810df5aca84bc460ac832710726d714e9dc..ff3705c608f8da5d1ac19d23bb0cc3317dcc28f6 100644 --- a/src/mol-base/_spec/collections.spec.ts +++ b/src/mol-base/_spec/collections.spec.ts @@ -9,6 +9,7 @@ import IntTuple from '../collections/int-tuple' import * as Sort from '../collections/sort' import OrderedSet from '../collections/ordered-set' import LinkedIndex from '../collections/linked-index' +import EquivalenceClasses from '../collections/equivalence-classes' function iteratorToArray<T>(it: Iterator<T>): T[] { const ret = []; @@ -312,4 +313,15 @@ describe('linked-index', () => { expect(index.has(0)).toBe(false); expect(index.has(1)).toBe(false); }); +}); + +describe('equiv-classes', () => { + it('integer mod classes', () => { + const cls = EquivalenceClasses<number, number>(x => x % 2, (a, b) => (a - b) % 2 === 0); + for (let i = 0; i < 6; i++) cls.add(i, i); + + expect(cls.groups.length).toBe(2); + expect(cls.groups[0]).toEqual([0, 2, 4]); + expect(cls.groups[1]).toEqual([1, 3, 5]); + }); }); \ No newline at end of file diff --git a/src/mol-base/collections/equivalence-classes.ts b/src/mol-base/collections/equivalence-classes.ts new file mode 100644 index 0000000000000000000000000000000000000000..2cf5ab8d7a3ae8b45e0ed23a76085da20a374931 --- /dev/null +++ b/src/mol-base/collections/equivalence-classes.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> + */ + +class EquivalenceClassesImpl<K, V> { + private id = 0; + private byHash: { [hash: number]: { id: number, keys: K[], value: V }[] } = Object.create(null); + + readonly groups: K[][] = []; + + private createGroup(key: K, value: V) { + const id = this.id++; + const keys = [key]; + this.groups[id] = keys; + return { id, keys, value }; + } + + add(key: K, a: V) { + const hash = this.getHash(a); + if (this.byHash[hash]) { + const groups = this.byHash[hash]; + for (const group of groups) { + if (this.areEqual(a, group.value)) { + group.keys[group.keys.length] = key; + return group.id; + } + } + const group = this.createGroup(key, a); + groups[groups.length] = group; + return group.id; + } else { + const group = this.createGroup(key, a); + this.byHash[hash] = [group]; + return group.id; + } + } + + constructor(private getHash: (v: V) => any, private areEqual: (a: V, b: V) => boolean) { } +} + +function EquivalenceClasses<K, V>(getHash: (x: V) => any, areEqual: (a: V, b: V) => boolean) { + return new EquivalenceClassesImpl<K, V>(getHash, areEqual); +} + +export default EquivalenceClasses; \ No newline at end of file diff --git a/src/mol-base/math/linear-algebra.ts b/src/mol-base/math/linear-algebra.ts index 10f1ea6663d49255841b2be4bf84d0ca841b8b1d..909c199bac88d653ffc9f6d5056b26464e3097ab 100644 --- a/src/mol-base/math/linear-algebra.ts +++ b/src/mol-base/math/linear-algebra.ts @@ -5,9 +5,9 @@ */ /* - * This code has been modified from https://github.com/toji/gl-matrix/, + * This code has been modified from https://github.com/toji/gl-matrix/, * copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights diff --git a/src/mol-data/model.ts b/src/mol-data/model.ts new file mode 100644 index 0000000000000000000000000000000000000000..0711c4f44de2dfcf15e02d407fbd32f263abb665 --- /dev/null +++ b/src/mol-data/model.ts @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +// TODO: define property accessor intefaces, graphs, spatial lookups and what have you. + +interface Model { + +} + +export default Model diff --git a/src/mol-data/structure.ts b/src/mol-data/structure.ts new file mode 100644 index 0000000000000000000000000000000000000000..5902cd55b5c2a648d3bb5f31679a6b5dab285ab4 --- /dev/null +++ b/src/mol-data/structure.ts @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Vec3, Mat4 } from '../mol-base/math/linear-algebra' +import AtomSet from './atom-set' +import Model from './model' + +export type Operator = + | { kind: Operator.Kind.Identity } + | { kind: Operator.Kind.Symmetry, hkl: number[], index: number, name: string, transform: Mat4, inverse: Mat4 } + | { kind: Operator.Kind.Assembly, assemblyName: string, index: number, transform: Mat4, inverse: Mat4 } + | { kind: Operator.Kind.Custom, name: string, transform: Mat4, inverse: Mat4 } + +export namespace Operator { + export enum Kind { Identity, Symmetry, Assembly, Custom } +} + +export interface Unit extends Readonly<{ + // Structure-level unique identifier of the unit. + id: number, + + // Each unit can only contain atoms from a single "chain" + // the reason for this is to make symmetry and assembly transforms fast + // without having to look at the actual contents of the unit + // multiple units can point to the same chain + chainIndex: number, + + // Provides access to the underlying data. + model: Model, + + // Determines the operation applied to this unit. + // The transform and and inverse a baked into the "getPosition" function + operator: Operator +}> { + // returns the untransformed position. Used for spatial queries. + getInvariantPosition(atom: number, slot: Vec3): Vec3 + + // gets the transformed position of the specified atom + getPosition(atom: number, slot: Vec3): Vec3 +} + +export interface Structure { units: { [id: number]: Unit }, atoms: AtomSet } + +export namespace Structure { + export const Empty: Structure = { units: {}, atoms: AtomSet.Empty }; + + export enum Algebra { AddUnit, RemoveUnit, UpdateConformation /* specify which units map to which */ } +} + +// export interface Selection { structure: Structure, sets: AtomSet[] } +// type SelectionImpl = Structure | Structure[] \ No newline at end of file diff --git a/src/mol-io/reader/cif/text/parser.ts b/src/mol-io/reader/cif/text/parser.ts index a328b8e6c9ac5be1d328b1ad026da13911257a78..4426877dce5007c6135b8c7d02d96b261c619b1c 100644 --- a/src/mol-io/reader/cif/text/parser.ts +++ b/src/mol-io/reader/cif/text/parser.ts @@ -567,7 +567,7 @@ async function parseInternal(data: string, ctx: Computation.Context) { // Data block if (token === CifTokenType.Data) { if (inSaveFrame) { - return error(tokenizer.lineNumber, "Unexpected data block inside a save frame."); + return error(tokenizer.lineNumber, 'Unexpected data block inside a save frame.'); } if (Object.keys(blockCategories).length > 0) { dataBlocks.push(Data.Block(blockCategories, blockHeader, saveFrames)); @@ -586,7 +586,7 @@ async function parseInternal(data: string, ctx: Computation.Context) { inSaveFrame = false; } else { if (inSaveFrame) { - return error(tokenizer.lineNumber, "Save frames cannot be nested."); + return error(tokenizer.lineNumber, 'Save frames cannot be nested.'); } inSaveFrame = true; saveCategories = Object.create(null); @@ -613,7 +613,7 @@ async function parseInternal(data: string, ctx: Computation.Context) { // Check if the latest save frame was closed. if (inSaveFrame) { - return error(tokenizer.lineNumber, "Unfinished save frame (`" + saveFrame.header + "`)."); + return error(tokenizer.lineNumber, 'Unfinished save frame (`' + saveFrame.header + '`).'); } if (Object.keys(blockCategories).length > 0) {