diff --git a/src/mol-base/collections/_spec/table.spec.ts b/src/mol-base/collections/_spec/table.spec.ts index 537579793d9bcc799abc9443d854ed28f3f7e3aa..aa7662ab48736aad7e2be9c8c6c815c66dc4b6af 100644 --- a/src/mol-base/collections/_spec/table.spec.ts +++ b/src/mol-base/collections/_spec/table.spec.ts @@ -40,8 +40,9 @@ describe('column', () => { expect(numStr.toArray()).toEqual(['1', '2']); }); - it('permutation', () => { - expect(Column.permutation(arr, [1, 0, 3, 2]).toArray()).toEqual([2, 1, 4, 3]); + it('view', () => { + expect(Column.view(arr, [1, 0, 3, 2]).toArray()).toEqual([2, 1, 4, 3]); + expect(Column.view(arr, [1, 3]).toArray()).toEqual([2, 4]); }); it('map to array', () => { diff --git a/src/mol-base/collections/column.ts b/src/mol-base/collections/column.ts index 941e25afcc9a390b861d31bdc5744b45817d58c5..e510026600e82f5e08c7b7e739b159f492da262f 100644 --- a/src/mol-base/collections/column.ts +++ b/src/mol-base/collections/column.ts @@ -12,12 +12,13 @@ interface Column<T> { readonly rowCount: number, value(row: number): T, valueKind(row: number): Column.ValueKind, - toArray(params?: Column.ToArrayParams): ReadonlyArray<T>, + toArray(params?: Column.ToArrayParams<T>): ReadonlyArray<T>, areValuesEqual(rowA: number, rowB: number): boolean } namespace Column { export type Type<T = any> = Type.Str | Type.Int | Type.Float | Type.Vector | Type.Matrix | Type.Aliased<T> + export type ArrayCtor<T> = { new(size: number): ArrayLike<T> } export namespace Type { export type Str = { T: string, kind: 'str' } @@ -37,8 +38,8 @@ namespace Column { export function aliased<T>(t: Type): Aliased<T> { return t as any as Aliased<T>; } } - export interface ToArrayParams { - array?: { new(size: number): ArrayLike<number> }, + export interface ToArrayParams<T> { + array?: ArrayCtor<T>, start?: number, /** Last row (exclusive) */ end?: number @@ -85,7 +86,7 @@ namespace Column { return windowColumn(column, start, end); } - export function permutation<T>(column: Column<T>, indices: ArrayLike<number>, checkIndentity = true) { + export function view<T>(column: Column<T>, indices: ArrayLike<number>, checkIndentity = true) { return columnPermutation(column, indices, checkIndentity); } @@ -94,12 +95,12 @@ namespace Column { return createFirstIndexMapOfColumn(column); } - export function mapToArray<T, S>(column: Column<T>, f: (v: T) => S, ctor?: { new(size: number): ArrayLike<number> }): ArrayLike<S> { + export function mapToArray<T, S>(column: Column<T>, f: (v: T) => S, ctor?: ArrayCtor<S>): ArrayLike<S> { return mapToArrayImpl(column, f, ctor || Array); } /** Makes the column backned by an array. Useful for columns that accessed often. */ - export function asArrayColumn<T>(c: Column<T>, array?: ToArrayParams['array']): Column<T> { + export function asArrayColumn<T>(c: Column<T>, array?: ArrayCtor<T>): Column<T> { if (c['@array']) return c; if (!c.isDefined) return Undefined(c.rowCount, c['@type']) as any as Column<T>; return arrayColumn({ array: c.toArray({ array }), type: c['@type'] as any, valueKind: c.valueKind }); @@ -220,7 +221,8 @@ function windowFull<T>(c: Column<T>, start: number, end: number): Column<T> { }; } -function isIdentity(map: ArrayLike<number>) { +function isIdentity(map: ArrayLike<number>, rowCount: number) { + if (map.length !== rowCount) return false; for (let i = 0, _i = map.length; i < _i; i++) { if (map[i] !== i) return false; } @@ -229,22 +231,22 @@ function isIdentity(map: ArrayLike<number>) { function columnPermutation<T>(c: Column<T>, map: ArrayLike<number>, checkIdentity: boolean): Column<T> { if (!c.isDefined) return c; - if (checkIdentity && isIdentity(map)) return c; - if (!c['@array']) return permutationArray(c, map); - return permutationFull(c, map); + if (checkIdentity && isIdentity(map, c.rowCount)) return c; + if (!c['@array']) return arrayView(c, map); + return viewFull(c, map); } -function permutationArray<T>(c: Column<T>, map: ArrayLike<number>): Column<T> { +function arrayView<T>(c: Column<T>, map: ArrayLike<number>): Column<T> { const array = c['@array']!; - const ret = new (array as any).constructor(c.rowCount); - for (let i = 0, _i = c.rowCount; i < _i; i++) ret[i] = array[map[i]]; + const ret = new (array as any).constructor(map.length); + for (let i = 0, _i = map.length; i < _i; i++) ret[i] = array[map[i]]; return arrayColumn({ array: ret, type: c['@type'], valueKind: c.valueKind }); } -function permutationFull<T>(c: Column<T>, map: ArrayLike<number>): Column<T> { +function viewFull<T>(c: Column<T>, map: ArrayLike<number>): Column<T> { const v = c.value, vk = c.valueKind, ave = c.areValuesEqual; const value: Column<T>['value'] = row => v(map[row]); - const rowCount = c.rowCount; + const rowCount = map.length; return { '@type': c['@type'], '@array': void 0, @@ -261,20 +263,20 @@ function permutationFull<T>(c: Column<T>, map: ArrayLike<number>): Column<T> { }; } -function mapToArrayImpl<T, S>(c: Column<T>, f: (v: T) => S, ctor: { new(size: number): ArrayLike<number> }): ArrayLike<S> { +function mapToArrayImpl<T, S>(c: Column<T>, f: (v: T) => S, ctor: Column.ArrayCtor<S>): ArrayLike<S> { const ret = new ctor(c.rowCount) as any; for (let i = 0, _i = c.rowCount; i < _i; i++) ret[i] = f(c.value(i)); return ret; } export namespace ColumnHelpers { - export function getArrayBounds(rowCount: number, params?: Column.ToArrayParams) { + export function getArrayBounds(rowCount: number, params?: Column.ToArrayParams<any>) { const start = params && typeof params.start !== 'undefined' ? Math.max(Math.min(params.start, rowCount - 1), 0) : 0; const end = params && typeof params.end !== 'undefined' ? Math.min(params.end, rowCount) : rowCount; return { start, end }; } - export function createArray(rowCount: number, params?: Column.ToArrayParams) { + export function createArray(rowCount: number, params?: Column.ToArrayParams<any>) { const c = params && typeof params.array !== 'undefined' ? params.array : Array; const { start, end } = getArrayBounds(rowCount, params); return { array: new c(end - start) as any[], start, end }; @@ -285,7 +287,7 @@ export namespace ColumnHelpers { return target; } - export function createAndFillArray(rowCount: number, value: (row: number) => any, params?: Column.ToArrayParams) { + export function createAndFillArray(rowCount: number, value: (row: number) => any, params?: Column.ToArrayParams<any>) { const { array, start } = createArray(rowCount, params); return fillArrayValues(value, array, start); } @@ -294,7 +296,7 @@ export namespace ColumnHelpers { return !!data.buffer && typeof data.byteLength === 'number' && typeof data.BYTES_PER_ELEMENT === 'number'; } - export function typedArrayWindow(data: any, params?: Column.ToArrayParams): ReadonlyArray<number> { + export function typedArrayWindow(data: any, params?: Column.ToArrayParams<any>): ReadonlyArray<number> { const { constructor, buffer, length, byteOffset, BYTES_PER_ELEMENT } = data; const { start, end } = getArrayBounds(length, params); if (start === 0 && end === length) return data; diff --git a/src/mol-base/collections/integer/_spec/segmentation.spec.ts b/src/mol-base/collections/integer/_spec/segmentation.spec.ts index c77fbb86ff81b0e8d7fbfacd27b64d2bcb9b8144..2c08ce9293b77d5c793f567ff457472d4a260530 100644 --- a/src/mol-base/collections/integer/_spec/segmentation.spec.ts +++ b/src/mol-base/collections/integer/_spec/segmentation.spec.ts @@ -19,12 +19,17 @@ describe('segments', () => { expect(p).toBe(Interval.ofBounds(0, 2)) }); + it('ofOffsetts', () => { + const p = Segmentation.ofOffsets([10, 12], Interval.ofBounds(10, 14)); + expect(p.segments).toEqual(new Int32Array([0, 2, 4])) + }); + it('map', () => { const segs = Segmentation.create([0, 1, 2]); expect(segs.segmentMap).toEqual(new Int32Array([0, 1])); expect(Segmentation.getSegment(segs, 0)).toBe(0); expect(Segmentation.getSegment(segs, 1)).toBe(1); - }) + }); it('iteration', () => { const it = Segmentation.transientSegments(segs, data); diff --git a/src/mol-base/collections/integer/impl/segmentation.ts b/src/mol-base/collections/integer/impl/segmentation.ts index a821db8ae880befef3c9f668722ef22b3ec08a0f..254191098fcaaf6932cb653565b3ea4f08e20e81 100644 --- a/src/mol-base/collections/integer/impl/segmentation.ts +++ b/src/mol-base/collections/integer/impl/segmentation.ts @@ -10,7 +10,7 @@ import Interval from '../interval' import SortedArray from '../sorted-array' import Segs from '../segmentation' -type Segmentation = { segments: SortedArray, segmentMap: ArrayLike<number>, count: number } +type Segmentation = { segments: SortedArray, segmentMap: Int32Array, count: number } export function create(values: ArrayLike<number>): Segmentation { const segments = SortedArray.ofSortedArray(values); @@ -24,6 +24,16 @@ export function create(values: ArrayLike<number>): Segmentation { return { segments, segmentMap, count: values.length - 1 }; } +export function ofOffsets(offsets: ArrayLike<number>, bounds: Interval): Segmentation { + const s = Interval.start(bounds); + const segments = new Int32Array(offsets.length + 1); + for (let i = 0, _i = offsets.length; i < _i; i++) { + segments[i] = offsets[i] - s; + } + segments[offsets.length] = Interval.end(bounds) - s; + return create(segments); +} + export function count({ count }: Segmentation) { return count; } export function getSegment({ segmentMap }: Segmentation, value: number) { return segmentMap[value]; } diff --git a/src/mol-base/collections/integer/segmentation.ts b/src/mol-base/collections/integer/segmentation.ts index 3be8cc7c345bc8be093020576abdbc21f55877f0..275b44f6dacc6e97de063b157692ef1241d6dd6b 100644 --- a/src/mol-base/collections/integer/segmentation.ts +++ b/src/mol-base/collections/integer/segmentation.ts @@ -13,6 +13,7 @@ namespace Segmentation { export interface Segment { index: number, start: number, end: number } export const create: (segs: ArrayLike<number>) => Segmentation = Impl.create as any; + export const ofOffsets: (offsets: ArrayLike<number>, bounds: Interval) => Segmentation = Impl.ofOffsets as any; export const count: (segs: Segmentation) => number = Impl.count as any; export const getSegment: (segs: Segmentation, value: number) => number = Impl.getSegment as any; diff --git a/src/mol-base/collections/table.ts b/src/mol-base/collections/table.ts index 087edc66b7f8f054432c0f24d7bc510fac41d6d0..fad8188538d32cdfdfa7faa03d4e76a55b23ca17 100644 --- a/src/mol-base/collections/table.ts +++ b/src/mol-base/collections/table.ts @@ -83,7 +83,7 @@ namespace Table { ret._rowCount = table._rowCount; ret._columns = table._columns; for (const c of table._columns) { - ret[c] = Column.permutation((table as any)[c], indices, false); + ret[c] = Column.view((table as any)[c], indices, false); } return ret; } diff --git a/src/mol-data/model.ts b/src/mol-data/model.ts index a9e450a0affa6b279eaccead06ddcc39de8eddb2..db02acf8aa71c13249c452eb500693b51834e39b 100644 --- a/src/mol-data/model.ts +++ b/src/mol-data/model.ts @@ -5,9 +5,8 @@ */ import * as Formats from './model/formats' -//import CommonProperties from './model/properties/common' -import MacromoleculeTree from './model/properties/macromolecule-tree' -import Conformation from './model/properties/conformation' +import MacromoleculeData from './model/data/macromolecule' +import ConformationData from './model/data/conformation' import Segmentation from '../mol-base/collections/integer/segmentation' /** @@ -22,9 +21,8 @@ interface Model extends Readonly<{ sourceData: Formats.RawData, - //common: CommonProperties, - macromolecule: MacromoleculeTree, - conformation: Conformation, + macromolecule: MacromoleculeData, + conformation: ConformationData, // used for diffing. version: { @@ -35,8 +33,7 @@ interface Model extends Readonly<{ atomCount: number, segments: Readonly<{ chains: Segmentation, - residues: Segmentation, - entities: Segmentation + residues: Segmentation }> }> { } diff --git a/src/mol-data/model/builders/mmcif.ts b/src/mol-data/model/builders/mmcif.ts index e9a26ff171bf7d642e9467c727124489d7a362e1..24ad91e491a3827f9c358bbfe431c9a73263029a 100644 --- a/src/mol-data/model/builders/mmcif.ts +++ b/src/mol-data/model/builders/mmcif.ts @@ -7,6 +7,7 @@ import { RawData } from '../formats' import { Frame as mmCIF } from '../../../mol-io/reader/cif/schema/mmcif' import Model from '../../model' +//import Column from '../../../mol-base/collections/column' import Interval from '../../../mol-base/collections/integer/interval' import Segmentation from '../../../mol-base/collections/integer/segmentation' import uuId from '../../../mol-base/utils/uuid' @@ -20,40 +21,28 @@ function findModelBounds(data: mmCIF, startIndex: number) { return Interval.ofBounds(startIndex, endIndex); } -function segment(data: mmCIF, bounds: Interval) { +function segmentOffsets(data: mmCIF, bounds: Interval) { const start = Interval.start(bounds), end = Interval.end(bounds); - const residues = [0], chains = [0], entities = [0]; + const residues = [start], chains = [start]; const { label_entity_id, auth_asym_id, auth_seq_id, pdbx_PDB_ins_code, label_comp_id } = data.atom_site; - let offset = 1; for (let i = start + 1; i < end; i++) { - const newEntity = !label_entity_id.areValuesEqual(i - 1, i); - const newChain = newEntity || !auth_asym_id.areValuesEqual(i - 1, i); + const newChain = !label_entity_id.areValuesEqual(i - 1, i) || !auth_asym_id.areValuesEqual(i - 1, i); const newResidue = newChain || !auth_seq_id.areValuesEqual(i - 1, i) || !pdbx_PDB_ins_code.areValuesEqual(i - 1, i) || !label_comp_id.areValuesEqual(i - 1, i); - if (newEntity) entities[entities.length] = offset; - if (newResidue) residues[residues.length] = offset; - if (newChain) chains[chains.length] = offset; - offset++; + if (newResidue) residues[residues.length] = i; + if (newChain) chains[chains.length] = i; } - residues[residues.length] = offset; - chains[chains.length] = offset; - entities[entities.length] = offset; - - return { - residues: Segmentation.create(residues), - chains: Segmentation.create(chains), - entities: Segmentation.create(entities) - }; + return { residues, chains }; } function createModel(raw: RawData, data: mmCIF, bounds: Interval): Model { - const segments = segment(data, bounds); + const segments = segmentOffsets(data, bounds); return { id: uuId(), sourceData: raw, @@ -63,7 +52,10 @@ function createModel(raw: RawData, data: mmCIF, bounds: Interval): Model { conformation: 0 as any, version: { data: 0, conformation: 0 }, atomCount: Interval.size(bounds), - segments + segments: { + residues: Segmentation.ofOffsets(segments.residues, bounds), + chains: Segmentation.ofOffsets(segments.chains, bounds), + } }; } diff --git a/src/mol-data/model/properties/conformation.ts b/src/mol-data/model/data/conformation.ts similarity index 100% rename from src/mol-data/model/properties/conformation.ts rename to src/mol-data/model/data/conformation.ts diff --git a/src/mol-data/model/properties/macromolecule-tree.ts b/src/mol-data/model/data/macromolecule.ts similarity index 100% rename from src/mol-data/model/properties/macromolecule-tree.ts rename to src/mol-data/model/data/macromolecule.ts diff --git a/src/mol-data/model/properties/secondary-structure.ts b/src/mol-data/model/data/secondary-structure.ts similarity index 100% rename from src/mol-data/model/properties/secondary-structure.ts rename to src/mol-data/model/data/secondary-structure.ts diff --git a/src/mol-data/model/properties/transforms.ts b/src/mol-data/model/data/transforms.ts similarity index 100% rename from src/mol-data/model/properties/transforms.ts rename to src/mol-data/model/data/transforms.ts diff --git a/src/mol-io/reader/cif/data-model.ts b/src/mol-io/reader/cif/data-model.ts index d353c534a9b4e573282cc8bf0dcf6b71bf7718a5..3d8c6edf42b20ebef5f940c392c81bab6792f92d 100644 --- a/src/mol-io/reader/cif/data-model.ts +++ b/src/mol-io/reader/cif/data-model.ts @@ -69,9 +69,9 @@ export interface Field { areValuesEqual(rowA: number, rowB: number): boolean, - toStringArray(params?: Column.ToArrayParams): ReadonlyArray<string>, - toIntArray(params?: Column.ToArrayParams): ReadonlyArray<number>, - toFloatArray(params?: Column.ToArrayParams): ReadonlyArray<number> + toStringArray(params?: Column.ToArrayParams<string>): ReadonlyArray<string>, + toIntArray(params?: Column.ToArrayParams<number>): ReadonlyArray<number>, + toFloatArray(params?: Column.ToArrayParams<number>): ReadonlyArray<number> } export function getMatrix(category: Category, field: string, rows: number, cols: number, row: number) {