diff --git a/data/mmcif-field-names.csv b/data/mmcif-field-names.csv index 9b27261b1a36147c031bc7a854bec91b664958f2..1d629c1fa923ee133a80caebaf4dc1e805c82b3d 100644 --- a/data/mmcif-field-names.csv +++ b/data/mmcif-field-names.csv @@ -62,6 +62,15 @@ entity.pdbx_mutation entity.pdbx_fragment entity.pdbx_ec +entity_poly.entity_id +entity_poly.type +entity_poly.nstd_linkage +entity_poly.nstd_monomer +entity_poly.pdbx_seq_one_letter_code +entity_poly.pdbx_seq_one_letter_code_can +entity_poly.pdbx_strand_id +entity_poly.pdbx_target_identifier + entity_poly_seq.entity_id entity_poly_seq.num entity_poly_seq.mon_id diff --git a/src/apps/cif2bcif/converter.ts b/src/apps/cif2bcif/converter.ts index 50ffc0e6e4b901a7d89d25dc1de00a0b3d8af701..d50e9acefdd6d28d79b03c7f582f96c345f8bcd9 100644 --- a/src/apps/cif2bcif/converter.ts +++ b/src/apps/cif2bcif/converter.ts @@ -24,15 +24,11 @@ async function getCIF(ctx: RuntimeContext, path: string) { return parsed.result; } -function getCategoryInstanceProvider(cat: CifCategory, fields: CifWriter.Field[]): CifWriter.Category.Provider { - return function (ctx: any) { - return { - data: cat, - name: cat.name, - fields, - rowCount: cat.rowCount - }; - } +function getCategoryInstanceProvider(cat: CifCategory, fields: CifWriter.Field[]): CifWriter.Category { + return { + name: cat.name, + instance: () => ({ data: cat, fields, rowCount: cat.rowCount }) + }; } export default function convert(path: string, asText = false) { diff --git a/src/apps/domain-annotation-server/mapping.ts b/src/apps/domain-annotation-server/mapping.ts index 581209fe517b69047e139686409d1cbe8f6e881a..1a8c64e01011dc39d5eb307ece2176bc156d7849 100644 --- a/src/apps/domain-annotation-server/mapping.ts +++ b/src/apps/domain-annotation-server/mapping.ts @@ -7,7 +7,7 @@ import { Table } from 'mol-data/db' import { CifWriter } from 'mol-io/writer/cif' import * as S from './schemas' -import { getCategoryInstanceProvider } from './utils' +//import { getCategoryInstanceProvider } from './utils' export default function create(allData: any) { const mols = Object.keys(allData); @@ -21,7 +21,7 @@ export default function create(allData: any) { const sources = getSources(data); if (!sources._rowCount) return enc.getData(); - enc.writeCategory(getCategoryInstanceProvider(`pdbx_domain_annotation_sources`, sources)); + enc.writeCategory({ name: `pdbx_domain_annotation_sources`, instance: () => CifWriter.Category.ofTable(sources) }); for (const cat of Object.keys(S.categories)) { writeDomain(enc, getDomain(cat, (S.categories as any)[cat], data)); @@ -38,8 +38,8 @@ type MappingRow = Table.Row<S.mapping>; function writeDomain(enc: CifWriter.Encoder, 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)); + enc.writeCategory({ name: `pdbx_${domain.name}_domain_annotation`, instance: () => CifWriter.Category.ofTable(domain.domains) }); + enc.writeCategory({ name: `pdbx_${domain.name}_domain_mapping`, instance: () => CifWriter.Category.ofTable(domain.mappings) }); } function getSources(data: any): Table<S.Sources> { diff --git a/src/apps/domain-annotation-server/utils.ts b/src/apps/domain-annotation-server/utils.ts deleted file mode 100644 index edd2011674a8f180fe982d91e6c47a1e7e52a160..0000000000000000000000000000000000000000 --- a/src/apps/domain-annotation-server/utils.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import { Table } from 'mol-data/db' -import { CifWriter } from 'mol-io/writer/cif' - -function columnValue(k: string) { - return (i: number, d: any) => d[k].value(i); -} - -function columnValueKind(k: string) { - return (i: number, d: any) => d[k].valueKind(i); -} - -function ofSchema(schema: Table.Schema) { - const fields: CifWriter.Field[] = []; - for (const k of Object.keys(schema)) { - const t = schema[k]; - const type: any = t.valueType === 'str' ? CifWriter.Field.Type.Str : t.valueType === 'int' ? CifWriter.Field.Type.Int : CifWriter.Field.Type.Float; - fields.push({ name: k, type, value: columnValue(k), valueKind: columnValueKind(k) }) - } - return fields; -} - -export function getCategoryInstanceProvider(name: string, table: Table<any>): CifWriter.Category.Provider { - return () => { - return { - data: table, - name, - fields: ofSchema(table._schema), - rowCount: table._rowCount - }; - } -} diff --git a/src/apps/structure-info/model.ts b/src/apps/structure-info/model.ts index b6a66fcf6aa54b9d06f42d02bd3bc5369ccfab43..c04627418a078014a7f89f061b8f0d5bbde55fef 100644 --- a/src/apps/structure-info/model.ts +++ b/src/apps/structure-info/model.ts @@ -9,7 +9,7 @@ import * as argparse from 'argparse' require('util.promisify').shim(); import { CifFrame } from 'mol-io/reader/cif' -import { Model, Structure, Element, Unit, Format, StructureProperties } from 'mol-model/structure' +import { Model, Structure, StructureElement, Unit, Format, StructureProperties } from 'mol-model/structure' // import { Run, Progress } from 'mol-task' import { OrderedSet } from 'mol-data/int'; import { openCif, downloadCif } from './helpers'; @@ -29,20 +29,20 @@ async function readPdbFile(path: string) { } export function atomLabel(model: Model, aI: number) { - const { atoms, residues, chains, residueSegments, chainSegments } = model.atomicHierarchy + const { atoms, residues, chains, residueAtomSegments, chainAtomSegments } = model.atomicHierarchy const { label_atom_id } = atoms const { label_comp_id, label_seq_id } = residues const { label_asym_id } = chains - const rI = residueSegments.segmentMap[aI] - const cI = chainSegments.segmentMap[aI] + const rI = residueAtomSegments.index[aI] + const cI = chainAtomSegments.index[aI] return `${label_asym_id.value(cI)} ${label_comp_id.value(rI)} ${label_seq_id.value(rI)} ${label_atom_id.value(aI)}` } export function residueLabel(model: Model, rI: number) { - const { residues, chains, residueSegments, chainSegments } = model.atomicHierarchy + const { residues, chains, residueAtomSegments, chainAtomSegments } = model.atomicHierarchy const { label_comp_id, label_seq_id } = residues const { label_asym_id } = chains - const cI = chainSegments.segmentMap[residueSegments.segments[rI]] + const cI = chainAtomSegments.index[residueAtomSegments.offsets[rI]] return `${label_asym_id.value(cI)} ${label_comp_id.value(rI)} ${label_seq_id.value(rI)}` } @@ -119,7 +119,7 @@ export function printSequence(model: Model) { export function printModRes(model: Model) { console.log('\nModified Residues\n============='); - const map = model.properties.modifiedResidueNameMap; + const map = model.properties.modifiedResidues.parentId; const { label_comp_id, _rowCount } = model.atomicHierarchy.residues; for (let i = 0; i < _rowCount; i++) { const comp_id = label_comp_id.value(i); @@ -146,7 +146,7 @@ export function printRings(structure: Structure) { export function printUnits(structure: Structure) { console.log('\nUnits\n============='); - const l = Element.Location(); + const l = StructureElement.create(); for (const unit of structure.units) { l.unit = unit; diff --git a/src/mol-data/db/column.ts b/src/mol-data/db/column.ts index d1c183b4c818b6b6de0b9677d2515ea717242a2b..55243135fd47da9c203215d019a012ab1c84dfe5 100644 --- a/src/mol-data/db/column.ts +++ b/src/mol-data/db/column.ts @@ -141,8 +141,8 @@ namespace Column { return createFirstIndexMapOfColumn(column); } - export function createIndexer<T>(column: Column<T>) { - return createIndexerOfColumn(column); + export function createIndexer<T, R extends number = number>(column: Column<T>) { + return createIndexerOfColumn(column) as ((e: T) => R); } export function mapToArray<T, S>(column: Column<T>, f: (v: T) => S, ctor?: ArrayCtor<S>): ArrayLike<S> { diff --git a/src/mol-data/int/_spec/segmentation.spec.ts b/src/mol-data/int/_spec/segmentation.spec.ts index f91eff82ac3ad56bdaa039d07a933faf015400bc..ef253062a74e3205f9a472fc0163ecea97384118 100644 --- a/src/mol-data/int/_spec/segmentation.spec.ts +++ b/src/mol-data/int/_spec/segmentation.spec.ts @@ -21,12 +21,12 @@ describe('segments', () => { it('ofOffsetts', () => { const p = Segmentation.ofOffsets([10, 12], Interval.ofBounds(10, 14)); - expect(p.segments).toEqual(new Int32Array([0, 2, 4])) + expect(p.offsets).toEqual(new Int32Array([0, 2, 4])) }); it('map', () => { const segs = Segmentation.create([0, 1, 2]); - expect(segs.segmentMap).toEqual(new Int32Array([0, 1])); + expect(segs.index).toEqual(new Int32Array([0, 1])); expect(Segmentation.getSegment(segs, 0)).toBe(0); expect(Segmentation.getSegment(segs, 1)).toBe(1); }); diff --git a/src/mol-data/int/impl/segmentation.ts b/src/mol-data/int/impl/segmentation.ts index 8959ead4cccea91fa012da525d5358139ef620e9..6ffb90b133053bb62c5e80021cc35b8aded7e49d 100644 --- a/src/mol-data/int/impl/segmentation.ts +++ b/src/mol-data/int/impl/segmentation.ts @@ -12,23 +12,23 @@ import Segs from '../segmentation' interface Segmentation { /** Segments stored as a sorted array */ - segments: SortedArray, + offsets: SortedArray, /** Mapping of values to segments */ - segmentMap: Int32Array, + index: Int32Array, /** Number of segments */ count: number } export function create(values: ArrayLike<number>): Segmentation { - const segments = SortedArray.ofSortedArray(values); - const max = SortedArray.max(segments); - const segmentMap = new Int32Array(max); + const offsets = SortedArray.ofSortedArray(values); + const max = SortedArray.max(offsets); + const index = new Int32Array(max); for (let i = 0, _i = values.length - 1; i < _i; i++) { for (let j = values[i], _j = values[i + 1]; j < _j; j++) { - segmentMap[j] = i; + index[j] = i; } } - return { segments, segmentMap, count: values.length - 1 }; + return { offsets, index, count: values.length - 1 }; } export function ofOffsets(offsets: ArrayLike<number>, bounds: Interval): Segmentation { @@ -43,26 +43,26 @@ export function ofOffsets(offsets: ArrayLike<number>, bounds: Interval): Segment /** Get number of segments in a segmentation */ export function count({ count }: Segmentation) { return count; } -export function getSegment({ segmentMap }: Segmentation, value: number) { return segmentMap[value]; } +export function getSegment({ index }: Segmentation, value: number) { return index[value]; } -export function projectValue({ segments }: Segmentation, set: OrderedSet, value: number): Interval { - const last = OrderedSet.max(segments); - const idx = value >= last ? -1 : OrderedSet.findPredecessorIndex(segments, value - 1); - return OrderedSet.findRange(set, OrderedSet.getAt(segments, idx), OrderedSet.getAt(segments, idx + 1) - 1); +export function projectValue({ offsets }: Segmentation, set: OrderedSet, value: number): Interval { + const last = OrderedSet.max(offsets); + const idx = value >= last ? -1 : OrderedSet.findPredecessorIndex(offsets, value - 1); + return OrderedSet.findRange(set, OrderedSet.getAt(offsets, idx), OrderedSet.getAt(offsets, idx + 1) - 1); } -export class SegmentIterator<T extends number = number> implements Iterator<Segs.Segment<T>> { - private segmentMax = 0; +export class SegmentIterator<I extends number = number> implements Iterator<Segs.Segment<I>> { private segmentMin = 0; + private segmentMax = 0; private setRange = Interval.Empty; - private value: Segs.Segment<T> = { index: 0, start: 0 as T, end: 0 as T }; + private value: Segs.Segment<I> = { index: 0 as I, start: 0, end: 0 }; hasNext: boolean = false; move() { while (this.hasNext) { if (this.updateValue()) { - this.value.index = this.segmentMin++; + this.value.index = this.segmentMin++ as I; this.hasNext = this.segmentMax >= this.segmentMin && Interval.size(this.setRange) > 0; break; } else { @@ -76,8 +76,8 @@ export class SegmentIterator<T extends number = number> implements Iterator<Segs const segmentEnd = this.segments[this.segmentMin + 1]; // TODO: add optimized version for interval and array? const setEnd = OrderedSet.findPredecessorIndexInInterval(this.set, segmentEnd, this.setRange); - this.value.start = Interval.start(this.setRange) as T; - this.value.end = setEnd as T; + this.value.start = Interval.start(this.setRange); + this.value.end = setEnd; this.setRange = Interval.ofBounds(setEnd, Interval.end(this.setRange)); return setEnd > this.value.start; } @@ -93,7 +93,7 @@ export class SegmentIterator<T extends number = number> implements Iterator<Segs this.hasNext = this.segmentMax >= this.segmentMin; } - setSegment(segment: Segs.Segment<T>) { + setSegment(segment: Segs.Segment<number>) { this.setRange = Interval.ofBounds(segment.start, segment.end); this.updateSegmentRange(); } @@ -106,5 +106,5 @@ export class SegmentIterator<T extends number = number> implements Iterator<Segs export function segments(segs: Segmentation, set: OrderedSet, segment?: Segs.Segment) { const int = typeof segment !== 'undefined' ? Interval.ofBounds(segment.start, segment.end) : Interval.ofBounds(0, OrderedSet.size(set)); - return new SegmentIterator(segs.segments, segs.segmentMap, set, int); + return new SegmentIterator(segs.offsets, segs.index, set, int); } \ No newline at end of file diff --git a/src/mol-data/int/segmentation.ts b/src/mol-data/int/segmentation.ts index 111914201f1f8e620c9fe5ded7f4f10119045373..7062733eeaf6ae039febf1767eba5b886a9018cf 100644 --- a/src/mol-data/int/segmentation.ts +++ b/src/mol-data/int/segmentation.ts @@ -9,25 +9,27 @@ import OrderedSet from './ordered-set' import * as Impl from './impl/segmentation' namespace Segmentation { - export interface Segment<T extends number = number> { index: number, start: T, end: T } + export interface Segment<I extends number = number> { index: I, start: number, end: number } - export const create: <T extends number = number>(segs: ArrayLike<T>) => Segmentation<T> = Impl.create as any; - export const ofOffsets: <T extends number = number>(offsets: ArrayLike<T>, bounds: Interval) => Segmentation<T> = Impl.ofOffsets as any; + export const create: <T extends number = number, I extends number = number>(segs: ArrayLike<T>) => Segmentation<T, I> = Impl.create as any; + export const ofOffsets: <T extends number = number, I extends number = number>(offsets: ArrayLike<T>, bounds: Interval) => Segmentation<T, I> = Impl.ofOffsets as any; - export const count: <T extends number = number>(segs: Segmentation<T>) => number = Impl.count as any; - export const getSegment: <T extends number = number>(segs: Segmentation<T>, value: T) => number = Impl.getSegment as any; - export const projectValue: <T extends number = number>(segs: Segmentation<T>, set: OrderedSet<T>, value: T) => Interval = Impl.projectValue as any; + export const count: <T extends number = number, I extends number = number>(segs: Segmentation<T, I>) => number = Impl.count as any; + export const getSegment: <T extends number = number, I extends number = number>(segs: Segmentation<T, I>, value: T) => number = Impl.getSegment as any; + export const projectValue: <T extends number = number, I extends number = number>(segs: Segmentation<T, I>, set: OrderedSet<T>, value: T) => Interval = Impl.projectValue as any; // Segment iterator that mutates a single segment object to mark all the segments. - export const transientSegments: <T extends number = number>(segs: Segmentation<T>, set: OrderedSet<T>, segment?: Segment<T>) => Impl.SegmentIterator<T> = Impl.segments as any; + export const transientSegments: <T extends number = number, I extends number = number>(segs: Segmentation<T, I>, set: OrderedSet<T>, segment?: Segment) => Impl.SegmentIterator<I> = Impl.segments as any; - export type Iterator<T extends number = number> = Impl.SegmentIterator<T> + export type SegmentIterator<I extends number = number> = Impl.SegmentIterator<I> } -interface Segmentation<T extends number = number> { +interface Segmentation<T extends number = number, I extends number = number> { '@type': 'segmentation', - readonly segments: ArrayLike<T>, - readonly segmentMap: ArrayLike<number>, + /** All segments are defined by offsets [offsets[i], offsets[i + 1]) for i \in [0, count - 1] */ + readonly offsets: ArrayLike<T>, + /** Segment index of the i-th element */ + readonly index: ArrayLike<I>, readonly count: number } diff --git a/src/mol-data/int/sorted-ranges.ts b/src/mol-data/int/sorted-ranges.ts index 59bbe3a0c4eecb8deb150ab769dd6e08cb95d84e..0e7e5f139c50a8bde253e7d414dea78fe3752fa0 100644 --- a/src/mol-data/int/sorted-ranges.ts +++ b/src/mol-data/int/sorted-ranges.ts @@ -28,13 +28,13 @@ namespace SortedRanges { return new Iterator<T>(ranges, set) } - export class Iterator<T extends number = number> implements _Iterator<Segmentation.Segment<T>> { - private value: Segmentation.Segment<T> = { index: 0, start: 0 as T, end: 0 as T } + export class Iterator<I extends number = number> implements _Iterator<Segmentation.Segment<I>> { + private value: Segmentation.Segment<I> = { index: 0 as I, start: 0, end: 0 } private curIndex = 0 private maxIndex = 0 - private interval: Interval<T> - private curMin: T = 0 as T + private interval: Interval<I> + private curMin: I = 0 as I hasNext: boolean = false; @@ -43,9 +43,9 @@ namespace SortedRanges { } updateValue() { - this.value.index = this.curIndex / 2 - this.value.start = OrderedSet.findPredecessorIndex(this.set, this.ranges[this.curIndex]) as T - this.value.end = OrderedSet.findPredecessorIndex(this.set, this.ranges[this.curIndex + 1]) + 1 as T + this.value.index = this.curIndex / 2 as I + this.value.start = OrderedSet.findPredecessorIndex(this.set, this.ranges[this.curIndex]) + this.value.end = OrderedSet.findPredecessorIndex(this.set, this.ranges[this.curIndex + 1]) + 1 } move() { @@ -67,7 +67,7 @@ namespace SortedRanges { return (index % 2 === 1) ? index - 1 : index } - constructor(private ranges: SortedRanges<T>, private set: OrderedSet<T>) { + constructor(private ranges: SortedRanges<I>, private set: OrderedSet<I>) { if (ranges.length) { this.curIndex = this.getRangeIndex(OrderedSet.min(set)) this.maxIndex = Math.min(ranges.length - 2, this.getRangeIndex(OrderedSet.max(set))) diff --git a/src/mol-data/util/interval-iterator.ts b/src/mol-data/util/interval-iterator.ts index 8174a23f9b77f47941b0ab8f46ea2a8ea30d5830..1aee8046659ba74e3290c42cbde92939e4adb687 100644 --- a/src/mol-data/util/interval-iterator.ts +++ b/src/mol-data/util/interval-iterator.ts @@ -8,8 +8,8 @@ import { OrderedSet, Interval, Segmentation } from '../int'; /** Emits a segment of length one for each element in the interval that is also in the set */ -export class IntervalIterator<T extends number = number> implements Iterator<Segmentation.Segment<T>> { - private value: Segmentation.Segment<T> = { index: 0, start: 0 as T, end: 0 as T } +export class IntervalIterator<I extends number = number> implements Iterator<Segmentation.Segment<I>> { + private value: Segmentation.Segment<I> = { index: 0 as I, start: 0, end: 0 } private curIndex = 0 private maxIndex = 0 @@ -17,9 +17,9 @@ export class IntervalIterator<T extends number = number> implements Iterator<Seg hasNext: boolean = false; updateValue() { - this.value.index = this.curIndex - this.value.start = OrderedSet.findPredecessorIndex(this.set, Interval.getAt(this.interval, this.curIndex)) as T - this.value.end = this.value.start + 1 as T + this.value.index = this.curIndex as I + this.value.start = OrderedSet.findPredecessorIndex(this.set, Interval.getAt(this.interval, this.curIndex)) + this.value.end = this.value.start + 1 } move() { @@ -34,7 +34,7 @@ export class IntervalIterator<T extends number = number> implements Iterator<Seg return this.value; } - constructor(private interval: Interval<T>, private set: OrderedSet<T>) { + constructor(private interval: Interval<I>, private set: OrderedSet<I>) { if (Interval.size(interval)) { this.curIndex = Interval.findPredecessorIndex(interval, OrderedSet.min(set)) this.maxIndex = Interval.findPredecessorIndex(interval, OrderedSet.max(set)) diff --git a/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts b/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts index d7384ed968c6187e72089fc8c15e833f02cd2851..027cda9f811b8f377874089ff59c70ef2d377b3d 100644 --- a/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts +++ b/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts @@ -7,7 +7,7 @@ import { ValueCell } from 'mol-util/value-cell' import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object' -import { Unit, Element } from 'mol-model/structure'; +import { Unit, StructureElement } from 'mol-model/structure'; import { DefaultStructureProps, UnitsVisual } from '..'; import { RuntimeContext } from 'mol-task' import { createTransforms, createColors } from './util/common'; @@ -284,8 +284,8 @@ export function PolymerTraceVisual(): UnitsVisual<PolymerTraceProps> { const { objectId, instanceId, elementId } = pickingId if (renderObject.id === objectId) { const unit = currentGroup.units[instanceId] - const indices = OrderedSet.ofSingleton(elementId as Element.Index); - return Element.Loci([{ unit, indices }]) + const indices = OrderedSet.ofSingleton(elementId as StructureElement.UnitIndex); + return StructureElement.Loci([{ unit, indices }]) } return EmptyLoci }, diff --git a/src/mol-geo/representation/structure/visual/util/element.ts b/src/mol-geo/representation/structure/visual/util/element.ts index 9e498df7dfa40ae272894d786dbe86e692467687..50a44321e32420b54cf80f80a4ab28f2cbc140e7 100644 --- a/src/mol-geo/representation/structure/visual/util/element.ts +++ b/src/mol-geo/representation/structure/visual/util/element.ts @@ -5,7 +5,7 @@ */ import { Vec3 } from 'mol-math/linear-algebra'; -import { Unit, Element } from 'mol-model/structure'; +import { Unit, StructureElement } from 'mol-model/structure'; import { SizeTheme } from '../../../../theme'; import { RuntimeContext } from 'mol-task'; import { icosahedronVertexCount } from '../../../../primitive/icosahedron'; @@ -19,7 +19,7 @@ import { Interval, OrderedSet } from 'mol-data/int'; import { getPhysicalRadius } from '../../../../theme/structure/size/physical'; import { PickingId } from '../../../../util/picking'; -export function getElementRadius(unit: Unit, props: SizeTheme): Element.Property<number> { +export function getElementRadius(unit: Unit, props: SizeTheme): StructureElement.Property<number> { switch (props.name) { case 'uniform': return () => props.value @@ -30,7 +30,7 @@ export function getElementRadius(unit: Unit, props: SizeTheme): Element.Property } } -export async function createElementSphereMesh(ctx: RuntimeContext, unit: Unit, radius: Element.Property<number>, detail: number, mesh?: Mesh) { +export async function createElementSphereMesh(ctx: RuntimeContext, unit: Unit, radius: StructureElement.Property<number>, detail: number, mesh?: Mesh) { const { elements } = unit; const elementCount = elements.length; const vertexCount = elementCount * icosahedronVertexCount(detail) @@ -38,7 +38,7 @@ export async function createElementSphereMesh(ctx: RuntimeContext, unit: Unit, r const v = Vec3.zero() const pos = unit.conformation.invariantPosition - const l = Element.Location() + const l = StructureElement.create() l.unit = unit for (let i = 0; i < elementCount; i++) { @@ -64,7 +64,7 @@ export function markElement(tMarker: ValueCell<TextureImage>, group: Unit.Symmet if (isEveryLoci(loci)) { applyMarkerAction(array, 0, elementCount * instanceCount, action) changed = true - } else if (Element.isLoci(loci)) { + } else if (StructureElement.isLoci(loci)) { for (const e of loci.elements) { const unitIdx = Unit.findUnitById(e.unit.id, group.units) if (unitIdx !== -1) { @@ -96,8 +96,8 @@ export function getElementLoci(id: number, group: Unit.SymmetryGroup, pickingId: const { objectId, instanceId, elementId } = pickingId if (id === objectId) { const unit = group.units[instanceId] - const indices = OrderedSet.ofSingleton(elementId as Element.Index); - return Element.Loci([{ unit, indices }]) + const indices = OrderedSet.ofSingleton(elementId as StructureElement.UnitIndex); + return StructureElement.Loci([{ unit, indices }]) } return EmptyLoci } \ No newline at end of file diff --git a/src/mol-geo/representation/structure/visual/util/polymer.ts b/src/mol-geo/representation/structure/visual/util/polymer.ts index acd5a3addd911273979bb49bacbdbaea835856a5..5f58974e37c4ab8d684145b5307317c933d7e9e8 100644 --- a/src/mol-geo/representation/structure/visual/util/polymer.ts +++ b/src/mol-geo/representation/structure/visual/util/polymer.ts @@ -4,7 +4,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Unit, Element, StructureProperties, Model } from 'mol-model/structure'; +import { Unit, StructureElement, StructureProperties, Model, ElementIndex } from 'mol-model/structure'; import { Segmentation, OrderedSet, Interval } from 'mol-data/int'; import { MoleculeType, SecondaryStructureType } from 'mol-model/structure/model/types'; import Iterator from 'mol-data/iterator'; @@ -26,7 +26,7 @@ export function getPolymerElementCount(unit: Unit) { const polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), elements) switch (unit.kind) { case Unit.Kind.Atomic: - const residueIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueSegments, elements) + const residueIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, elements) while (polymerIt.hasNext) { const polymerSegment = polymerIt.move() residueIt.setSegment(polymerSegment) @@ -49,7 +49,7 @@ export function getPolymerElementCount(unit: Unit) { return count } -function getTraceName(l: Element.Location) { +function getTraceName(l: StructureElement) { const compId = StructureProperties.residue.label_comp_id(l) const chemCompMap = l.unit.model.properties.chemicalComponentMap const cc = chemCompMap.get(compId) @@ -63,7 +63,7 @@ function getTraceName(l: Element.Location) { return traceName } -function setTraceElement(l: Element.Location, residueSegment: Segmentation.Segment<Element>) { +function setTraceElement(l: StructureElement, residueSegment: Segmentation.Segment) { const elements = l.unit.elements l.element = elements[residueSegment.start] const traceName = getTraceName(l) @@ -94,7 +94,8 @@ function getTraceName2(model: Model, residueModelIndex: number) { return traceName } -function getTraceElement2(model: Model, residueModelSegment: Segmentation.Segment<Element>) { +// TODO fix type +function getTraceElement2(model: Model, residueModelSegment: Segmentation.Segment<number>) { const traceName = getTraceName2(model, residueModelSegment.index) for (let j = residueModelSegment.start, _j = residueModelSegment.end; j < _j; j++) { @@ -130,7 +131,8 @@ function getDirectionName2(model: Model, residueModelIndex: number) { // return `${label_asym_id.value(cI)} ${label_comp_id.value(rI)} ${label_seq_id.value(rI)}` // } -function getDirectionElement2(model: Model, residueModelSegment: Segmentation.Segment<Element>) { +// TODO fix type +function getDirectionElement2(model: Model, residueModelSegment: Segmentation.Segment<number>) { const traceName = getDirectionName2(model, residueModelSegment.index) for (let j = residueModelSegment.start, _j = residueModelSegment.end; j < _j; j++) { @@ -152,8 +154,8 @@ export function PolymerBackboneIterator(unit: Unit): Iterator<PolymerBackbonePai } interface PolymerBackbonePair { - centerA: Element.Location - centerB: Element.Location + centerA: StructureElement + centerB: StructureElement indexA: number indexB: number posA: Vec3 @@ -162,8 +164,8 @@ interface PolymerBackbonePair { function createPolymerBackbonePair (unit: Unit) { return { - centerA: Element.Location(unit), - centerB: Element.Location(unit), + centerA: StructureElement.create(unit), + centerB: StructureElement.create(unit), indexA: 0, indexB: 0, posA: Vec3.zero(), @@ -176,9 +178,9 @@ const enum AtomicPolymerBackboneIteratorState { nextPolymer, firstResidue, nextR export class AtomicPolymerBackboneIterator<T extends number = number> implements Iterator<PolymerBackbonePair> { private value: PolymerBackbonePair - private polymerIt: SortedRanges.Iterator<Element> - private residueIt: Segmentation.Iterator<Element> - private polymerSegment: Segmentation.Segment<Element> + private polymerIt: SortedRanges.Iterator<ElementIndex> + private residueIt: Segmentation.SegmentIterator<number> // TODO specific type + private polymerSegment: Segmentation.Segment<ElementIndex> private state: AtomicPolymerBackboneIteratorState = AtomicPolymerBackboneIteratorState.nextPolymer private pos: SymmetryOperator.CoordinateMapper @@ -228,10 +230,10 @@ export class AtomicPolymerBackboneIterator<T extends number = number> implements } constructor(unit: Unit.Atomic) { - const { residueSegments } = unit.model.atomicHierarchy + const { residueAtomSegments } = unit.model.atomicHierarchy // console.log('unit.elements', OrderedSet.toArray(unit.elements)) this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements) - this.residueIt = Segmentation.transientSegments(residueSegments, unit.elements) + this.residueIt = Segmentation.transientSegments(residueAtomSegments, unit.elements) this.pos = unit.conformation.invariantPosition this.value = createPolymerBackbonePair(unit) @@ -244,8 +246,8 @@ const enum CoarsePolymerBackboneIteratorState { nextPolymer, nextElement } export class CoarsePolymerBackboneIterator<T extends number = number> implements Iterator<PolymerBackbonePair> { private value: PolymerBackbonePair - private polymerIt: SortedRanges.Iterator<Element> - private polymerSegment: Segmentation.Segment<Element> + private polymerIt: SortedRanges.Iterator<ElementIndex> + private polymerSegment: Segmentation.Segment<ElementIndex> private state: CoarsePolymerBackboneIteratorState = CoarsePolymerBackboneIteratorState.nextPolymer private pos: SymmetryOperator.CoordinateMapper private elementIndex: number @@ -321,7 +323,7 @@ export function PolymerTraceIterator(unit: Unit): Iterator<PolymerTraceElement> } interface PolymerTraceElement { - center: Element.Location + center: StructureElement index: number first: boolean last: boolean @@ -339,7 +341,7 @@ interface PolymerTraceElement { function createPolymerTraceElement (unit: Unit): PolymerTraceElement { return { - center: Element.Location(unit), + center: StructureElement.create(unit), index: 0, first: false, last: false, @@ -358,13 +360,13 @@ function createPolymerTraceElement (unit: Unit): PolymerTraceElement { const enum AtomicPolymerTraceIteratorState { nextPolymer, nextResidue } -function setSegment (outSegment: Segmentation.Segment<Element>, index: number, segments: Segmentation<Element>, min: number, max: number): Segmentation.Segment<Element> { +function setSegment (outSegment: Segmentation.Segment<number>, index: number, segments: Segmentation<number>, min: number, max: number): Segmentation.Segment<number> { // index = Math.min(Math.max(0, index), segments.segments.length - 2) const _index = Math.min(Math.max(min, index), max) if (isNaN(_index)) console.log(_index, index, min, max) outSegment.index = _index - outSegment.start = segments.segments[_index] - outSegment.end = segments.segments[_index + 1] + outSegment.start = segments.offsets[_index] + outSegment.end = segments.offsets[_index + 1] // console.log(index, {...outSegment}, {...boundingSegment}, segments.segments[boundingSegment.index]) return outSegment } @@ -372,14 +374,14 @@ function setSegment (outSegment: Segmentation.Segment<Element>, index: number, s export class AtomicPolymerTraceIterator<T extends number = number> implements Iterator<PolymerTraceElement> { private value: PolymerTraceElement - private polymerIt: SortedRanges.Iterator<Element> - private residueIt: Segmentation.Iterator<Element> + private polymerIt: SortedRanges.Iterator<ElementIndex> + private residueIt: Segmentation.SegmentIterator<number> // TODO specialize type private residueSegmentMin: number private residueSegmentMax: number private state: AtomicPolymerTraceIteratorState = AtomicPolymerTraceIteratorState.nextPolymer - private residueSegments: Segmentation<Element> + private residueSegments: Segmentation<ElementIndex> - private tmpSegment: Segmentation.Segment<Element> + private tmpSegment: Segmentation.Segment<number> private unit: Unit.Atomic @@ -391,12 +393,12 @@ export class AtomicPolymerTraceIterator<T extends number = number> implements It target[2] = this.unit.model.atomicConformation.z[index] } - updateResidueSegmentRange(polymerSegment: Segmentation.Segment<Element>) { - const { polymerRanges, residueSegments } = this.unit.model.atomicHierarchy + updateResidueSegmentRange(polymerSegment: Segmentation.Segment<ElementIndex>) { + const { polymerRanges, residueAtomSegments } = this.unit.model.atomicHierarchy const sMin = polymerRanges[polymerSegment.index * 2] const sMax = polymerRanges[polymerSegment.index * 2 + 1] - this.residueSegmentMin = residueSegments.segmentMap[sMin] - this.residueSegmentMax = residueSegments.segmentMap[sMax] + this.residueSegmentMin = residueAtomSegments.index[sMin] + this.residueSegmentMax = residueAtomSegments.index[sMax] } move() { @@ -454,14 +456,14 @@ export class AtomicPolymerTraceIterator<T extends number = number> implements It } constructor(unit: Unit.Atomic) { - const { residueSegments } = unit.model.atomicHierarchy + const { residueAtomSegments } = unit.model.atomicHierarchy this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements) - this.residueIt = Segmentation.transientSegments(residueSegments, unit.elements); - this.residueSegments = residueSegments + this.residueIt = Segmentation.transientSegments(residueAtomSegments, unit.elements); + this.residueSegments = residueAtomSegments this.value = createPolymerTraceElement(unit) this.hasNext = this.residueIt.hasNext && this.polymerIt.hasNext - this.tmpSegment = { index: 0, start: 0 as Element, end: 0 as Element } + this.tmpSegment = { index: 0, start: 0 as ElementIndex, end: 0 as ElementIndex } this.unit = unit } diff --git a/src/mol-geo/theme/structure/color/chain-id.ts b/src/mol-geo/theme/structure/color/chain-id.ts index 0b0e13114d7614db98054ca521166d4272f43693..d703aa303e3822fbb65794ea4db9a842a8a1516c 100644 --- a/src/mol-geo/theme/structure/color/chain-id.ts +++ b/src/mol-geo/theme/structure/color/chain-id.ts @@ -4,13 +4,13 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Unit, StructureProperties, Element } from 'mol-model/structure'; +import { Unit, StructureProperties, StructureElement } from 'mol-model/structure'; import { StructureColorDataProps } from '.'; import { ColorData, createElementColor, createUniformColor } from '../../../util/color-data'; import { ColorScale } from 'mol-util/color'; -function getAsymId(unit: Unit): Element.Property<string> { +function getAsymId(unit: Unit): StructureElement.Property<string> { switch (unit.kind) { case Unit.Kind.Atomic: return StructureProperties.chain.label_asym_id @@ -21,7 +21,7 @@ function getAsymId(unit: Unit): Element.Property<string> { throw new Error('unhandled unit kind') } -export function chainIdColorData(props: StructureColorDataProps, locationFn: (l: Element.Location, renderElementIdx: number) => void, colorData?: ColorData) { +export function chainIdColorData(props: StructureColorDataProps, locationFn: (l: StructureElement, renderElementIdx: number) => void, colorData?: ColorData) { const { group: { units }, elementCount } = props const unit = units[0] @@ -32,7 +32,7 @@ export function chainIdColorData(props: StructureColorDataProps, locationFn: (l: const scale = ColorScale.create({ domain }) const asym_id = getAsymId(unit) - const l = Element.Location() + const l = StructureElement.create() l.unit = unit return createElementColor({ @@ -46,7 +46,7 @@ export function chainIdColorData(props: StructureColorDataProps, locationFn: (l: export function chainIdElementColorData(props: StructureColorDataProps, colorData?: ColorData) { const elements = props.group.units[0].elements - function locationFn(l: Element.Location, renderElementIdx: number) { + function locationFn(l: StructureElement, renderElementIdx: number) { l.element = elements[renderElementIdx] } return chainIdColorData(props, locationFn, colorData) @@ -55,11 +55,11 @@ export function chainIdElementColorData(props: StructureColorDataProps, colorDat export function chainIdLinkColorData(props: StructureColorDataProps, colorData?: ColorData): ColorData { const unit = props.group.units[0] const elements = unit.elements - let locationFn: (l: Element.Location, renderElementIdx: number) => void + let locationFn: (l: StructureElement, renderElementIdx: number) => void switch (unit.kind) { case Unit.Kind.Atomic: const { a } = unit.links - locationFn = (l: Element.Location, renderElementIdx: number) => { + locationFn = (l: StructureElement, renderElementIdx: number) => { l.element = elements[a[renderElementIdx]] } return chainIdColorData(props, locationFn, colorData) diff --git a/src/mol-geo/theme/structure/size/physical.ts b/src/mol-geo/theme/structure/size/physical.ts index 34ac86d0de5e92e49b1c659a0ffa704d3489d500..712427e2006dc24e4b366cdf608039e6ec1454fd 100644 --- a/src/mol-geo/theme/structure/size/physical.ts +++ b/src/mol-geo/theme/structure/size/physical.ts @@ -4,11 +4,11 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Element, Unit, StructureProperties } from 'mol-model/structure'; +import { StructureElement, Unit, StructureProperties } from 'mol-model/structure'; import { StructureSizeDataProps } from '.'; import { createAttributeSize } from '../../../util/size-data'; -export function getPhysicalRadius(unit: Unit): Element.Property<number> { +export function getPhysicalRadius(unit: Unit): StructureElement.Property<number> { if (Unit.isAtomic(unit)) { return StructureProperties.atom.vdw_radius } else if (Unit.isSpheres(unit)) { @@ -27,7 +27,7 @@ export function physicalSizeData(factor: number, props: StructureSizeDataProps) const unit = group.units[0] const elements = group.elements; const radius = getPhysicalRadius(unit) - const l = Element.Location() + const l = StructureElement.create() l.unit = unit return createAttributeSize({ sizeFn: (elementIdx: number) => { diff --git a/src/mol-io/reader/cif/schema/mmcif.ts b/src/mol-io/reader/cif/schema/mmcif.ts index 3640bb29ccfacb083ad9b982e2b31d54eea536b2..9cb22fed0a19337c3de02b89930707220af57424 100644 --- a/src/mol-io/reader/cif/schema/mmcif.ts +++ b/src/mol-io/reader/cif/schema/mmcif.ts @@ -80,16 +80,26 @@ 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), + src_method: Aliased<'nat' | 'man' | 'syn'>(str), + formula_weight: float, pdbx_description: str, pdbx_number_of_molecules: float, pdbx_mutation: str, pdbx_fragment: str, pdbx_ec: List(',', x => x), + details: str, + }, + entity_poly: { + entity_id: str, + type: str, + nstd_linkage: Aliased<'no' | 'n' | 'yes' | 'y'>(str), + nstd_monomer: Aliased<'no' | 'n' | 'yes' | 'y'>(str), + pdbx_seq_one_letter_code: str, + pdbx_seq_one_letter_code_can: str, + pdbx_strand_id: str, + pdbx_target_identifier: str, }, entity_poly_seq: { entity_id: str, @@ -109,11 +119,11 @@ export const mmCIF_Schema = { title: str, }, struct_asym: { - details: str, - entity_id: str, id: str, + entity_id: str, pdbx_modified: str, pdbx_blank_PDB_chainid_flag: Aliased<'Y' | 'N'>(str), + details: str, }, struct_conf: { beg_label_asym_id: str, diff --git a/src/mol-io/writer/cif/encoder.ts b/src/mol-io/writer/cif/encoder.ts index 174ea0f8995780f61a2252a5d13dd66ea18a2f69..883f2aed440e97b3ec60ac62f72eff55d2e7e1a3 100644 --- a/src/mol-io/writer/cif/encoder.ts +++ b/src/mol-io/writer/cif/encoder.ts @@ -69,19 +69,19 @@ export namespace Field { } } -export interface Category<Key = any, Data = any> { +export interface Category<Ctx = any> { name: string, - fields: Field<Key, Data>[], - data?: Data, - rowCount: number, - keys?: () => Iterator<Key> + instance(ctx: Ctx): Category.Instance } export namespace Category { - export const Empty: Category = { name: 'empty', rowCount: 0, fields: [] }; + export const Empty: Instance = { fields: [], rowCount: 0 }; - export interface Provider<Ctx = any> { - (ctx: Ctx): Category + export interface Instance<Key = any, Data = any> { + fields: Field[], + data?: Data, + rowCount: number, + keys?: () => Iterator<Key> } export interface Filter { @@ -102,11 +102,11 @@ export namespace Category { getFormat(cat, field) { return void 0; } } - export function ofTable(name: string, table: Table<Table.Schema>, indices?: ArrayLike<number>): Category<number, Table<Table.Schema>> { + export function ofTable(table: Table<Table.Schema>, indices?: ArrayLike<number>): Category.Instance { if (indices) { - return { name, fields: cifFieldsFromTableSchema(table._schema), data: table, rowCount: indices.length, keys: () => Iterator.Array(indices) }; + return { fields: cifFieldsFromTableSchema(table._schema), data: table, rowCount: indices.length, keys: () => Iterator.Array(indices) }; } - return { name, fields: cifFieldsFromTableSchema(table._schema), data: table, rowCount: table._rowCount }; + return { fields: cifFieldsFromTableSchema(table._schema), data: table, rowCount: table._rowCount }; } } @@ -115,7 +115,7 @@ export interface Encoder<T = string | Uint8Array> extends EncoderBase { setFormatter(formatter?: Category.Formatter): void, startDataBlock(header: string): void, - writeCategory<Ctx>(category: Category.Provider<Ctx>, contexts?: Ctx[]): void, + writeCategory<Ctx>(category: Category<Ctx>, contexts?: Ctx[]): void, getData(): T } @@ -123,7 +123,7 @@ export namespace Encoder { export function writeDatabase(encoder: Encoder, name: string, database: Database<Database.Schema>) { encoder.startDataBlock(name); for (const table of database._tableNames) { - encoder.writeCategory(() => Category.ofTable(table, database[table])); + encoder.writeCategory({ name: table, instance: () => Category.ofTable(database[table]) }); } } diff --git a/src/mol-io/writer/cif/encoder/binary.ts b/src/mol-io/writer/cif/encoder/binary.ts index 01e07965d0295e503d134c3d2adbe19dd44667d5..37b15515faf0e19ffcd5033ba429bad784157fe5 100644 --- a/src/mol-io/writer/cif/encoder/binary.ts +++ b/src/mol-io/writer/cif/encoder/binary.ts @@ -38,7 +38,7 @@ export default class BinaryEncoder implements Encoder<Uint8Array> { }); } - writeCategory<Ctx>(category: Category.Provider<Ctx>, contexts?: Ctx[]) { + writeCategory<Ctx>(category: Category<Ctx>, contexts?: Ctx[]) { if (!this.data) { throw new Error('The writer contents have already been encoded, no more writing.'); } @@ -47,23 +47,23 @@ export default class BinaryEncoder implements Encoder<Uint8Array> { throw new Error('No data block created.'); } - const src = !contexts || !contexts.length ? [category(<any>void 0)] : contexts.map(c => category(c)); - const categories = src.filter(c => c && c.rowCount > 0); - if (!categories.length) return; - if (!this.filter.includeCategory(categories[0].name)) return; + if (!this.filter.includeCategory(category.name)) return; - const count = categories.reduce((a, c) => a + c.rowCount, 0); + const src = !contexts || !contexts.length ? [category.instance(<any>void 0)] : contexts.map(c => category.instance(c)); + const instances = src.filter(c => c && c.rowCount > 0); + if (!instances.length) return; + + const count = instances.reduce((a, c) => a + c.rowCount, 0); if (!count) return; - const first = categories[0]!; - 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) })); - const fields = getIncludedFields(first); + const cat: EncodedCategory = { name: '_' + category.name, columns: [], rowCount: count }; + const data = instances.map(c => ({ data: c.data, keys: () => c.keys ? c.keys() : Iterator.Range(0, c.rowCount - 1) })); + const fields = getIncludedFields(instances[0]); for (const f of fields) { - if (!this.filter.includeField(first.name, f.name)) continue; + if (!this.filter.includeField(category.name, f.name)) continue; - const format = this.formatter.getFormat(first.name, f.name); + const format = this.formatter.getFormat(category.name, f.name); cat.columns.push(encodeField(f, data, count, getArrayCtor(f, format), getEncoder(f, format))); } // no columns included. diff --git a/src/mol-io/writer/cif/encoder/text.ts b/src/mol-io/writer/cif/encoder/text.ts index a191627285c2297171ddaa6e3a1ff31177985b50..6d695bfc9f0605e6591d0ae4bf9e9ebb9413d513 100644 --- a/src/mol-io/writer/cif/encoder/text.ts +++ b/src/mol-io/writer/cif/encoder/text.ts @@ -33,7 +33,7 @@ export default class TextEncoder implements Encoder<string> { StringBuilder.write(this.builder, `data_${(header || '').replace(/[ \n\t]/g, '').toUpperCase()}\n#\n`); } - writeCategory<Ctx>(category: Category.Provider<Ctx>, contexts?: Ctx[]) { + writeCategory<Ctx>(category: Category<Ctx>, contexts?: Ctx[]) { if (this.encoded) { throw new Error('The writer contents have already been encoded, no more writing.'); } @@ -42,18 +42,20 @@ export default class TextEncoder implements Encoder<string> { throw new Error('No data block created.'); } - const categories = !contexts || !contexts.length ? [category(<any>void 0)] : contexts.map(c => category(c)); - if (!categories.length) return; - if (!this.filter.includeCategory(categories[0].name)) return; + if (!this.filter.includeCategory(category.name)) return; - const rowCount = categories.reduce((v, c) => v + c.rowCount, 0); + const src = !contexts || !contexts.length ? [category.instance(<any>void 0)] : contexts.map(c => category.instance(c)); + const instances = src.filter(c => c && c.rowCount > 0); + if (!instances.length) return; + + const rowCount = instances.reduce((v, c) => v + c.rowCount, 0); if (rowCount === 0) return; if (rowCount === 1) { - writeCifSingleRecord(categories[0]!, this.builder, this.filter, this.formatter); + writeCifSingleRecord(category, instances[0]!, this.builder, this.filter, this.formatter); } else { - writeCifLoop(categories, this.builder, this.filter, this.formatter); + writeCifLoop(category, instances, this.builder, this.filter, this.formatter); } } @@ -108,19 +110,19 @@ function getFloatPrecisions(categoryName: string, fields: Field[], formatter: Ca return ret; } -function writeCifSingleRecord(category: Category<any>, builder: StringBuilder, filter: Category.Filter, formatter: Category.Formatter) { - const fields = getIncludedFields(category); - const data = category.data; +function writeCifSingleRecord(category: Category, instance: Category.Instance, builder: StringBuilder, filter: Category.Filter, formatter: Category.Formatter) { + const fields = getIncludedFields(instance); + const data = instance.data; let width = fields.reduce((w, f) => filter.includeField(category.name, f.name) ? Math.max(w, f.name.length) : 0, 0); // this means no field from this category is included. if (width === 0) return; width += category.name.length + 6; - const it = category.keys ? category.keys() : Iterator.Range(0, category.rowCount - 1); + const it = instance.keys ? instance.keys() : Iterator.Range(0, instance.rowCount - 1); const key = it.move(); - const precisions = getFloatPrecisions(category.name, category.fields, formatter); + const precisions = getFloatPrecisions(category.name, instance.fields, formatter); for (let _f = 0; _f < fields.length; _f++) { const f = fields[_f]; @@ -133,28 +135,27 @@ function writeCifSingleRecord(category: Category<any>, builder: StringBuilder, f StringBuilder.write(builder, '#\n'); } -function writeCifLoop(categories: Category[], builder: StringBuilder, filter: Category.Filter, formatter: Category.Formatter) { - const first = categories[0]; - const fieldSource = getIncludedFields(first); - const fields = filter === Category.DefaultFilter ? fieldSource : fieldSource.filter(f => filter.includeField(first.name, f.name)); +function writeCifLoop(category: Category, instances: Category.Instance[], builder: StringBuilder, filter: Category.Filter, formatter: Category.Formatter) { + const fieldSource = getIncludedFields(instances[0]); + const fields = filter === Category.DefaultFilter ? fieldSource : fieldSource.filter(f => filter.includeField(category.name, f.name)); const fieldCount = fields.length; if (fieldCount === 0) return; - const precisions = getFloatPrecisions(first.name, fields, formatter); + const precisions = getFloatPrecisions(category.name, fields, formatter); writeLine(builder, 'loop_'); for (let i = 0; i < fieldCount; i++) { - writeLine(builder, `_${first.name}.${fields[i].name}`); + writeLine(builder, `_${category.name}.${fields[i].name}`); } let index = 0; - for (let _c = 0; _c < categories.length; _c++) { - const category = categories[_c]; - const data = category.data; + for (let _c = 0; _c < instances.length; _c++) { + const instance = instances[_c]; + const data = instance.data; - if (category.rowCount === 0) continue; + if (instance.rowCount === 0) continue; - const it = category.keys ? category.keys() : Iterator.Range(0, category.rowCount - 1); + const it = instance.keys ? instance.keys() : Iterator.Range(0, instance.rowCount - 1); while (it.hasNext) { const key = it.move(); diff --git a/src/mol-io/writer/cif/encoder/util.ts b/src/mol-io/writer/cif/encoder/util.ts index af8b3369f61d11939cca26f24b3bae02a0de4a6a..48e80cc4063ac1973eea10e3888478451b0e39ad 100644 --- a/src/mol-io/writer/cif/encoder/util.ts +++ b/src/mol-io/writer/cif/encoder/util.ts @@ -11,7 +11,7 @@ export function getFieldDigitCount(field: Field) { return 6; } -export function getIncludedFields(category: Category<any, any>) { +export function getIncludedFields(category: Category.Instance) { return category.fields.some(f => !!f.shouldInclude) ? category.fields.filter(f => !f.shouldInclude || f.shouldInclude(category.data)) : category.fields; diff --git a/src/mol-model/loci.ts b/src/mol-model/loci.ts index 85b9fa4deae88a448ffcd192b2d2b52faad27a55..c235548f98a8905ede4371d801d7fe25b17decdc 100644 --- a/src/mol-model/loci.ts +++ b/src/mol-model/loci.ts @@ -4,7 +4,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Element } from './structure' +import { StructureElement } from './structure' import { Link } from './structure/structure/unit/links' /** A Loci that includes every loci */ @@ -21,4 +21,4 @@ export function isEmptyLoci(x: any): x is EmptyLoci { return !!x && x.kind === 'empty-loci'; } -export type Loci = Element.Loci | Link.Loci | EveryLoci | EmptyLoci \ No newline at end of file +export type Loci = StructureElement.Loci | Link.Loci | EveryLoci | EmptyLoci \ No newline at end of file diff --git a/src/mol-model/structure/export/categories/atom_site.ts b/src/mol-model/structure/export/categories/atom_site.ts index c5b1fab70a58a5784148c82a2702321356edc533..7e49d127a88174871dde8377d92a1b3f090c6b89 100644 --- a/src/mol-model/structure/export/categories/atom_site.ts +++ b/src/mol-model/structure/export/categories/atom_site.ts @@ -6,13 +6,13 @@ */ import { CifWriter } from 'mol-io/writer/cif'; -import { Element, Structure, StructureProperties as P } from '../../structure'; +import { StructureElement, Structure, StructureProperties as P } from '../../structure'; import { CifExportContext } from '../mmcif'; import CifField = CifWriter.Field import CifCategory = CifWriter.Category import E = CifWriter.Encodings -const atom_site_fields: CifField<Element.Location>[] = [ +const atom_site_fields: CifField<StructureElement>[] = [ CifField.str('group_PDB', P.residue.group_PDB), CifField.index('id'), CifField.str('type_symbol', P.atom.type_symbol as any), @@ -38,17 +38,19 @@ const atom_site_fields: CifField<Element.Location>[] = [ CifField.str('auth_asym_id', P.chain.auth_asym_id), CifField.int('pdbx_PDB_model_num', P.unit.model_num, { encoder: E.deltaRLE }), - CifField.str<Element.Location, Structure>('operator_name', P.unit.operator_name, { - shouldInclude: structure => structure.units.some(u => !u.conformation.operator.isIdentity) + CifField.str<StructureElement, Structure>('operator_name', P.unit.operator_name, { + shouldInclude: structure => { console.log(!!structure); return structure.units.some(u => !u.conformation.operator.isIdentity) } }) ]; -export function _atom_site({ structure }: CifExportContext): CifCategory { - return { - data: structure, - name: 'atom_site', - fields: atom_site_fields, - rowCount: structure.elementCount, - keys: () => structure.elementLocations() +export const _atom_site: CifCategory<CifExportContext> = { + name: 'atom_site', + instance({ structure }: CifExportContext) { + return { + fields: atom_site_fields, + data: structure, + rowCount: structure.elementCount, + keys: () => structure.elementLocations() + }; } } \ No newline at end of file diff --git a/src/mol-model/structure/export/categories/modified-residues.ts b/src/mol-model/structure/export/categories/modified-residues.ts new file mode 100644 index 0000000000000000000000000000000000000000..52886186c68ad95c6d22af825227b7a381429094 --- /dev/null +++ b/src/mol-model/structure/export/categories/modified-residues.ts @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2017-2018 Mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Segmentation } from 'mol-data/int'; +import { CifWriter } from 'mol-io/writer/cif'; +import { StructureElement, StructureProperties as P, Unit } from '../../../structure'; +import { CifExportContext } from '../mmcif'; + +import CifField = CifWriter.Field +import CifCategory = CifWriter.Category + +const pdbx_struct_mod_residue_fields: CifField<number, StructureElement[]>[] = [ + CifField.index('id'), + CifField.str(`label_comp_id`, (i, xs) => P.residue.label_comp_id(xs[i])), + CifField.int(`label_seq_id`, (i, xs) => P.residue.label_seq_id(xs[i])), + CifField.str(`pdbx_PDB_ins_code`, (i, xs) => P.residue.pdbx_PDB_ins_code(xs[i])), + CifField.str(`label_asym_id`, (i, xs) => P.chain.label_asym_id(xs[i])), + CifField.str(`label_entity_id`, (i, xs) => P.chain.label_entity_id(xs[i])), + CifField.str(`auth_comp_id`, (i, xs) => P.residue.auth_comp_id(xs[i])), + CifField.int(`auth_seq_id`, (i, xs) => P.residue.auth_seq_id(xs[i])), + CifField.str(`auth_asym_id`, (i, xs) => P.chain.auth_asym_id(xs[i])), + CifField.str<number, StructureElement[]>('parent_comp_id', (i, xs) => xs[i].unit.model.properties.modifiedResidues.parentId.get(P.residue.label_comp_id(xs[i]))!), + CifField.str('details', (i, xs) => xs[i].unit.model.properties.modifiedResidues.details.get(P.residue.label_comp_id(xs[i]))!) +]; + +function getModifiedResidues({ model, structure }: CifExportContext): StructureElement[] { + const map = model.properties.modifiedResidues.parentId; + if (!map.size) return []; + + const ret = []; + const prop = P.residue.label_comp_id; + const loc = StructureElement.create(); + for (const unit of structure.units) { + if (!Unit.isAtomic(unit) || !unit.conformation.operator.isIdentity) continue; + const residues = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements); + loc.unit = unit; + while (residues.hasNext) { + const seg = residues.move(); + loc.element = unit.elements[seg.start]; + const name = prop(loc); + if (map.has(name)) { + ret[ret.length] = StructureElement.create(loc.unit, loc.element); + } + } + } + return ret; +} + +export const _pdbx_struct_mod_residue: CifCategory<CifExportContext> = { + name: 'pdbx_struct_mod_residue', + instance(ctx) { + const residues = getModifiedResidues(ctx); + return { fields: pdbx_struct_mod_residue_fields, data: residues, rowCount: residues.length }; + } +} \ No newline at end of file diff --git a/src/mol-model/structure/export/categories/secondary-structure.ts b/src/mol-model/structure/export/categories/secondary-structure.ts index 35936e1652cc3da117ebabbe1a95d0b855f06662..1c18813be27b4607095b9bf0749be9b092658bfe 100644 --- a/src/mol-model/structure/export/categories/secondary-structure.ts +++ b/src/mol-model/structure/export/categories/secondary-structure.ts @@ -7,36 +7,36 @@ import { Segmentation } from 'mol-data/int'; import { CifWriter } from 'mol-io/writer/cif'; import { SecondaryStructure } from '../../model/properties/seconday-structure'; -import { Element, Unit, StructureProperties as P } from '../../structure'; +import { StructureElement, Unit, StructureProperties as P } from '../../structure'; import { CifExportContext } from '../mmcif'; - import CifField = CifWriter.Field import CifCategory = CifWriter.Category import { Column } from 'mol-data/db'; -export function _struct_conf(ctx: CifExportContext): CifCategory { - const elements = findElements(ctx, 'helix'); - return { - data: elements, - name: 'struct_conf', - fields: struct_conf_fields, - rowCount: elements.length - }; -} +export const _struct_conf: CifCategory<CifExportContext> = { + name: 'struct_conf', + instance(ctx) { + const elements = findElements(ctx, 'helix'); + return { fields: struct_conf_fields, data: elements, rowCount: elements.length }; + } +}; -export function _struct_sheet_range(ctx: CifExportContext): CifCategory { - const elements = findElements(ctx, 'sheet'); - return { - data: elements, - name: 'struct_sheet_range', - fields: struct_sheet_range_fields, - rowCount: elements.length - }; -} +export const _struct_sheet_range: CifCategory<CifExportContext> = { + name: 'struct_sheet_range', + instance(ctx) { + const elements = (findElements(ctx, 'sheet') as SSElement<SecondaryStructure.Sheet>[]).sort(compare_ssr); + return { fields: struct_sheet_range_fields, data: elements, rowCount: elements.length }; + } +}; + +function compare_ssr(x: SSElement<SecondaryStructure.Sheet>, y: SSElement<SecondaryStructure.Sheet>) { + const a = x.element, b = y.element; + return a.sheet_id < b.sheet_id ? -1 : a.sheet_id === b.sheet_id ? x.start.element - y.start.element : 1 +}; const struct_conf_fields: CifField[] = [ CifField.str<number, SSElement<SecondaryStructure.Helix>[]>('conf_type_id', (i, data) => data[i].element.type_id), - CifField.str<number, SSElement<SecondaryStructure.Helix>[]>('conf_type_id', (i, data, idx) => `${data[i].element.type_id}${idx + 1}`), + CifField.str<number, SSElement<SecondaryStructure.Helix>[]>('id', (i, data, idx) => `${data[i].element.type_id}${idx + 1}`), ...residueIdFields('beg_', e => e.start), ...residueIdFields('end_', e => e.end), CifField.str<number, SSElement<SecondaryStructure.Helix>[]>('pdbx_PDB_helix_class', (i, data) => data[i].element.helix_class), @@ -47,20 +47,20 @@ const struct_conf_fields: CifField[] = [ ]; const struct_sheet_range_fields: CifField[] = [ - CifField.index('id'), CifField.str<number, SSElement<SecondaryStructure.Sheet>[]>('sheet_id', (i, data) => data[i].element.sheet_id), + CifField.index('id'), ...residueIdFields('beg_', e => e.start), ...residueIdFields('end_', e => e.end), CifField.str('symmetry', (i, data) => '', { valueKind: (i, d) => Column.ValueKind.Unknown }) ]; -function residueIdFields(prefix: string, loc: (e: SSElement<any>) => Element.Location): CifField<number, SSElement<SecondaryStructure.Helix>[]>[] { +function residueIdFields(prefix: string, loc: (e: SSElement<any>) => StructureElement): CifField<number, SSElement<SecondaryStructure.Helix>[]>[] { return [ CifField.str(`${prefix}label_comp_id`, (i, d) => P.residue.label_comp_id(loc(d[i]))), CifField.int(`${prefix}label_seq_id`, (i, d) => P.residue.label_seq_id(loc(d[i]))), CifField.str(`pdbx_${prefix}PDB_ins_code`, (i, d) => P.residue.pdbx_PDB_ins_code(loc(d[i]))), CifField.str(`${prefix}label_asym_id`, (i, d) => P.chain.label_asym_id(loc(d[i]))), - CifField.str(`${prefix}_entity_id`, (i, d) => P.chain.label_entity_id(loc(d[i]))), + CifField.str(`${prefix}label_entity_id`, (i, d) => P.chain.label_entity_id(loc(d[i]))), CifField.str(`${prefix}auth_comp_id`, (i, d) => P.residue.auth_comp_id(loc(d[i]))), CifField.int(`${prefix}auth_seq_id`, (i, d) => P.residue.auth_seq_id(loc(d[i]))), CifField.str(`${prefix}auth_asym_id`, (i, d) => P.chain.auth_asym_id(loc(d[i]))) @@ -68,8 +68,8 @@ function residueIdFields(prefix: string, loc: (e: SSElement<any>) => Element.Loc } interface SSElement<T extends SecondaryStructure.Element> { - start: Element.Location, - end: Element.Location, + start: StructureElement, + end: StructureElement, length: number, element: T } @@ -83,10 +83,10 @@ function findElements<T extends SecondaryStructure.Element>(ctx: CifExportContex // currently can only support this for "identity" operators. if (!Unit.isAtomic(unit) || !unit.conformation.operator.isIdentity) continue; - const segs = unit.model.atomicHierarchy.residueSegments; + const segs = unit.model.atomicHierarchy.residueAtomSegments; const residues = Segmentation.transientSegments(segs, unit.elements); - let current: Segmentation.Segment<Element>, move = true; + let current: Segmentation.Segment, move = true; while (residues.hasNext) { if (move) current = residues.move(); @@ -105,8 +105,8 @@ function findElements<T extends SecondaryStructure.Element>(ctx: CifExportContex if (startIdx !== key[current.index]) { move = false; ssElements[ssElements.length] = { - start: Element.Location(unit, segs.segments[start]), - end: Element.Location(unit, segs.segments[prev]), + start: StructureElement.create(unit, segs.offsets[start]), + end: StructureElement.create(unit, segs.offsets[prev]), length: prev - start + 1, element } diff --git a/src/mol-model/structure/export/mmcif.ts b/src/mol-model/structure/export/mmcif.ts index c783b552e041369ec50d4466e299357d476119c1..f8c9d7dcb661acc99d891efe430faeddf59c8b48 100644 --- a/src/mol-model/structure/export/mmcif.ts +++ b/src/mol-model/structure/export/mmcif.ts @@ -10,27 +10,34 @@ import { mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif' import { Structure } from '../structure' import { Model } from '../model' import { _atom_site } from './categories/atom_site'; +import CifCategory = CifWriter.Category +import { _struct_conf, _struct_sheet_range } from './categories/secondary-structure'; +import { _pdbx_struct_mod_residue } from './categories/modified-residues'; export interface CifExportContext { structure: Structure, - model: Model + model: Model, + cache: any } -import CifCategory = CifWriter.Category -import { _struct_conf, _struct_sheet_range } from './categories/secondary-structure'; - -function copy_mmCif_category(name: keyof mmCIF_Schema) { - return ({ model }: CifExportContext) => { - if (model.sourceData.kind !== 'mmCIF') return CifCategory.Empty; - const table = model.sourceData.data[name]; - if (!table || !table._rowCount) return CifCategory.Empty; - return CifCategory.ofTable(name, table); +function copy_mmCif_category(name: keyof mmCIF_Schema): CifCategory<CifExportContext> { + return { + name, + instance({ model }) { + if (model.sourceData.kind !== 'mmCIF') return CifCategory.Empty; + const table = model.sourceData.data[name]; + if (!table || !table._rowCount) return CifCategory.Empty; + return CifCategory.ofTable(table); + } }; } -function _entity({ model, structure }: CifExportContext): CifCategory { - const keys = Structure.getEntityKeys(structure); - return CifCategory.ofTable('entity', model.entities.data, keys); +const _entity: CifCategory<CifExportContext> = { + name: 'entity', + instance({ structure, model}) { + const keys = Structure.getEntityKeys(structure); + return CifCategory.ofTable(model.entities.data, keys); + } } const Categories = [ @@ -52,11 +59,18 @@ const Categories = [ _struct_conf, _struct_sheet_range, + // Sequence + copy_mmCif_category('struct_asym'), // TODO: filter only present chains? + copy_mmCif_category('entity_poly'), + copy_mmCif_category('entity_poly_seq'), + // Misc // TODO: filter for actual present residues? copy_mmCif_category('chem_comp'), copy_mmCif_category('atom_sites'), + _pdbx_struct_mod_residue, + // Atoms _atom_site ]; @@ -78,13 +92,13 @@ export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structure: S if (models.length !== 1) throw 'Can\'t export stucture composed from multiple models.'; const model = models[0]; - const ctx: CifExportContext[] = [{ structure, model }]; + const ctx: CifExportContext[] = [{ structure, model, cache: Object.create(null) }]; for (const cat of Categories) { encoder.writeCategory(cat, ctx); } for (const customProp of model.customProperties.all) { - const cats = customProp.cifExport.categoryProvider(ctx[0]); + const cats = customProp.cifExport.categories; for (const cat of cats) { encoder.writeCategory(cat, ctx); } diff --git a/src/mol-model/structure/model.ts b/src/mol-model/structure/model.ts index 0551a4c2098ebf6cb5ec74367fb37905d55b0500..c208d036d49c19603f1b93bf8960412644c44def 100644 --- a/src/mol-model/structure/model.ts +++ b/src/mol-model/structure/model.ts @@ -4,10 +4,11 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import Model from './model/model' +import { Model } from './model/model' import * as Types from './model/types' import Format from './model/format' import { ModelSymmetry } from './model/properties/symmetry' import StructureSequence from './model/properties/sequence' +export * from './model/indexing' export { Model, Types, Format, ModelSymmetry, StructureSequence } \ No newline at end of file diff --git a/src/mol-model/structure/model/formats/mmcif.ts b/src/mol-model/structure/model/formats/mmcif.ts index 11e899c90e65bdce4fc65332787f04346bc2850a..c18e3997b84bfd4a125d198e05f161d509c75944 100644 --- a/src/mol-model/structure/model/formats/mmcif.ts +++ b/src/mol-model/structure/model/formats/mmcif.ts @@ -11,7 +11,7 @@ import { Tensor, Vec3 } from 'mol-math/linear-algebra'; import { Task, RuntimeContext } from 'mol-task'; import UUID from 'mol-util/uuid'; import Format from '../format'; -import Model from '../model'; +import { Model } from '../model'; import { Entities } from '../properties/common'; import { CustomProperties } from '../properties/custom'; import { ModelSymmetry } from '../properties/symmetry'; @@ -70,18 +70,20 @@ function getNcsOperators(format: mmCIF_Format) { } return opers; } - -function getModifiedResidueNameMap(format: mmCIF_Format) { +function getModifiedResidueNameMap(format: mmCIF_Format): Model['properties']['modifiedResidues'] { const data = format.data.pdbx_struct_mod_residue; - const map = new Map<string, string>(); + const parentId = new Map<string, string>(); + const details = new Map<string, string>(); const comp_id = data.label_comp_id.isDefined ? data.label_comp_id : data.auth_comp_id; - const parent_id = data.parent_comp_id; + const parent_id = data.parent_comp_id, details_data = data.details; for (let i = 0; i < data._rowCount; i++) { - map.set(comp_id.value(i), parent_id.value(i)); + const id = comp_id.value(i); + parentId.set(id, parent_id.value(i)); + details.set(id, details_data.value(i)); } - return map; + return { parentId, details }; } function getAsymIdSerialMap(format: mmCIF_Format) { @@ -123,14 +125,14 @@ function getChemicalComponentMap(format: mmCIF_Format) { } export interface FormatData { - modifiedResidueNameMap: Map<string, string> + modifiedResidues: Model['properties']['modifiedResidues'] asymIdSerialMap: Map<string, number> chemicalComponentMap: Map<string, ChemicalComponent> } function getFormatData(format: mmCIF_Format): FormatData { return { - modifiedResidueNameMap: getModifiedResidueNameMap(format), + modifiedResidues: getModifiedResidueNameMap(format), asymIdSerialMap: getAsymIdSerialMap(format), chemicalComponentMap: getChemicalComponentMap(format) } @@ -154,7 +156,7 @@ function createStandardModel(format: mmCIF_Format, atom_site: AtomSite, entities modelNum: atom_site.pdbx_PDB_model_num.value(0), entities, symmetry: getSymmetry(format), - sequence: getSequence(format.data, entities, atomic.hierarchy, formatData.modifiedResidueNameMap), + sequence: getSequence(format.data, entities, atomic.hierarchy, formatData.modifiedResidues.parentId), atomicHierarchy: atomic.hierarchy, atomicConformation: atomic.conformation, coarseHierarchy: coarse.hierarchy, @@ -180,7 +182,7 @@ function createModelIHM(format: mmCIF_Format, data: IHMData, formatData: FormatD modelNum: data.model_id, entities: data.entities, symmetry: getSymmetry(format), - sequence: getSequence(format.data, data.entities, atomic.hierarchy, formatData.modifiedResidueNameMap), + sequence: getSequence(format.data, data.entities, atomic.hierarchy, formatData.modifiedResidues.parentId), atomicHierarchy: atomic.hierarchy, atomicConformation: atomic.conformation, coarseHierarchy: coarse.hierarchy, diff --git a/src/mol-model/structure/model/formats/mmcif/atomic.ts b/src/mol-model/structure/model/formats/mmcif/atomic.ts index 677914582d91d39e0806903bea388fa2914ad54a..db05907fcb8ee28852f7d1abbf93e883ea11c8e9 100644 --- a/src/mol-model/structure/model/formats/mmcif/atomic.ts +++ b/src/mol-model/structure/model/formats/mmcif/atomic.ts @@ -8,9 +8,9 @@ import { Column, Table } from 'mol-data/db'; import { Interval, Segmentation } from 'mol-data/int'; import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif'; import UUID from 'mol-util/uuid'; -import { Element } from '../../../../structure'; +import { ElementIndex } from '../../../../structure'; import Format from '../../format'; -import Model from '../../model'; +import { Model } from '../../model'; import { AtomicConformation, AtomicData, AtomicHierarchy, AtomicSegments, AtomsSchema, ChainsSchema, ResiduesSchema } from '../../properties/atomic'; import { getAtomicKeys } from '../../properties/utils/atomic-keys'; import { ElementSymbol } from '../../types'; @@ -26,11 +26,11 @@ function findHierarchyOffsets(atom_site: AtomSite) { if (atom_site._rowCount === 0) return { residues: [], chains: [] }; const start = 0, end = atom_site._rowCount; - const residues = [start as Element], chains = [start as Element]; + const residues = [start as ElementIndex], chains = [start as ElementIndex]; const { label_entity_id, label_asym_id, label_seq_id, auth_seq_id, pdbx_PDB_ins_code, label_comp_id } = atom_site; - for (let i = start + 1; i < end; i++) { + for (let i = start + 1 as ElementIndex; i < end; i++) { const newChain = !label_entity_id.areValuesEqual(i - 1, i) || !label_asym_id.areValuesEqual(i - 1, i); const newResidue = newChain || !label_seq_id.areValuesEqual(i - 1, i) @@ -38,8 +38,8 @@ function findHierarchyOffsets(atom_site: AtomSite) { || !pdbx_PDB_ins_code.areValuesEqual(i - 1, i) || !label_comp_id.areValuesEqual(i - 1, i); - if (newResidue) residues[residues.length] = i as Element; - if (newChain) chains[chains.length] = i as Element; + if (newResidue) residues[residues.length] = i as ElementIndex; + if (newChain) chains[chains.length] = i as ElementIndex; } return { residues, chains }; } @@ -92,8 +92,8 @@ export function getAtomicHierarchyAndConformation(format: mmCIF_Format, atom_sit } const hierarchySegments: AtomicSegments = { - residueSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, Interval.ofBounds(0, atom_site._rowCount)), - chainSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, Interval.ofBounds(0, atom_site._rowCount)), + residueAtomSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, Interval.ofBounds(0, atom_site._rowCount)), + chainAtomSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, Interval.ofBounds(0, atom_site._rowCount)), } const hierarchyKeys = getAtomicKeys(hierarchyData, entities, hierarchySegments); diff --git a/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts b/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts index b89b473df9e6dfccb1d0732099290c60a1c4382d..2785d960372256cf9352a9946d4e3228ec87012b 100644 --- a/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts +++ b/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts @@ -5,11 +5,11 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import Model from '../../../model' +import { Model } from '../../../model' import { LinkType } from '../../../types' import { ModelPropertyDescriptor } from '../../../properties/custom'; import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif'; -import { Structure, Unit, StructureProperties, Element } from '../../../../structure'; +import { Structure, Unit, StructureProperties, StructureElement } from '../../../../structure'; import { Segmentation } from 'mol-data/int'; import { CifWriter } from 'mol-io/writer/cif' @@ -22,22 +22,22 @@ export namespace ComponentBond { isStatic: true, name: 'chem_comp_bond', cifExport: { - categoryNames: ['chem_comp_bond'], - categoryProvider(ctx) { - const chem_comp_bond = getChemCompBond(ctx.model); - if (!chem_comp_bond) return []; - - const comp_names = getUniqueResidueNames(ctx.structure); - const { comp_id, _rowCount } = chem_comp_bond; - const indices: number[] = []; - for (let i = 0; i < _rowCount; i++) { - if (comp_names.has(comp_id.value(i))) indices[indices.length] = i; + categories: [{ + name: 'chem_comp_bond', + instance(ctx) { + const chem_comp_bond = getChemCompBond(ctx.model); + if (!chem_comp_bond) return CifWriter.Category.Empty; + + const comp_names = getUniqueResidueNames(ctx.structure); + const { comp_id, _rowCount } = chem_comp_bond; + const indices: number[] = []; + for (let i = 0; i < _rowCount; i++) { + if (comp_names.has(comp_id.value(i))) indices[indices.length] = i; + } + + return CifWriter.Category.ofTable(chem_comp_bond, indices) } - - return [ - () => CifWriter.Category.ofTable('chem_comp_bond', chem_comp_bond, indices) - ]; - } + }] } } @@ -134,14 +134,14 @@ export namespace ComponentBond { function getUniqueResidueNames(s: Structure) { const prop = StructureProperties.residue.label_comp_id; const names = new Set<string>(); - const loc = Element.Location(); + const loc = StructureElement.create(); for (const unit of s.units) { if (!Unit.isAtomic(unit)) continue; - const residues = Segmentation.transientSegments(unit.model.atomicHierarchy.residueSegments, unit.elements); + const residues = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements); loc.unit = unit; while (residues.hasNext) { const seg = residues.move(); - loc.element = seg.start; + loc.element = unit.elements[seg.start]; names.add(prop(loc)); } } diff --git a/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts b/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts index f2ea4703a18f1ef90982750a70c37a1036e963ab..933705171012573a68167af9657cfa80e7460fd1 100644 --- a/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts +++ b/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts @@ -5,8 +5,8 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import Model from '../../../model' -import { Element, Structure } from '../../../../structure' +import { Model } from '../../../model' +import { Structure } from '../../../../structure' import { LinkType } from '../../../types' import { findEntityIdByAsymId, findAtomIndexByLabelName } from '../util' import { Column } from 'mol-data/db' @@ -14,6 +14,7 @@ import { ModelPropertyDescriptor } from '../../../properties/custom'; import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif'; import { SortedArray } from 'mol-data/int'; import { CifWriter } from 'mol-io/writer/cif' +import { ElementIndex } from '../../../indexing'; export interface StructConn { getResidueEntries(residueAIndex: number, residueBIndex: number): ReadonlyArray<StructConn.Entry>, @@ -26,42 +27,42 @@ export namespace StructConn { isStatic: true, name: 'struct_conn', cifExport: { - categoryNames: ['struct_conn'], - categoryProvider(ctx) { - const struct_conn = getStructConn(ctx.model); - if (!struct_conn) return []; + categories: [{ + name: 'struct_conn', + instance(ctx) { + const struct_conn = getStructConn(ctx.model); + if (!struct_conn) return CifWriter.Category.Empty; - const strConn = get(ctx.model); - if (!strConn || strConn.entries.length === 0) return []; + const strConn = get(ctx.model); + if (!strConn || strConn.entries.length === 0) return CifWriter.Category.Empty; - const foundAtoms = new Set<Element>(); - const indices: number[] = []; - for (const entry of strConn.entries) { - const { partners } = entry; - let hasAll = true; - for (let i = 0, _i = partners.length; i < _i; i++) { - const atom = partners[i].atomIndex; - if (foundAtoms.has(atom)) continue; - if (hasAtom(ctx.structure, atom)) { - foundAtoms.add(atom); - } else { - hasAll = false; - break; + const foundAtoms = new Set<ElementIndex>(); + const indices: number[] = []; + for (const entry of strConn.entries) { + const { partners } = entry; + let hasAll = true; + for (let i = 0, _i = partners.length; i < _i; i++) { + const atom = partners[i].atomIndex; + if (foundAtoms.has(atom)) continue; + if (hasAtom(ctx.structure, atom)) { + foundAtoms.add(atom); + } else { + hasAll = false; + break; + } + } + if (hasAll) { + indices[indices.length] = entry.rowIndex; } } - if (hasAll) { - indices[indices.length] = entry.rowIndex; - } - } - return [ - () => CifWriter.Category.ofTable('struct_conn', struct_conn, indices) - ]; - } + return CifWriter.Category.ofTable(struct_conn, indices); + } + }] } } - function hasAtom({ units }: Structure, element: Element) { + function hasAtom({ units }: Structure, element: ElementIndex) { for (let i = 0, _i = units.length; i < _i; i++) { if (SortedArray.indexOf(units[i].elements, element) >= 0) return true; } @@ -132,7 +133,7 @@ export namespace StructConn { distance: number, order: number, flags: number, - partners: { residueIndex: number, atomIndex: Element, symmetry: string }[] + partners: { residueIndex: number, atomIndex: ElementIndex, symmetry: string }[] } type StructConnType = diff --git a/src/mol-model/structure/model/formats/mmcif/ihm.ts b/src/mol-model/structure/model/formats/mmcif/ihm.ts index d9b401d46ead15b02552f13ae2709ca23075e9be..4a331561302935489633992057edaa3b62925630 100644 --- a/src/mol-model/structure/model/formats/mmcif/ihm.ts +++ b/src/mol-model/structure/model/formats/mmcif/ihm.ts @@ -12,7 +12,7 @@ import { getCoarseKeys } from '../../properties/utils/coarse-keys'; import { UUID } from 'mol-util'; import { Segmentation, Interval } from 'mol-data/int'; import { Mat3, Tensor } from 'mol-math/linear-algebra'; -import { Element } from '../../../structure' +import { ElementIndex, ChainIndex } from '../../indexing'; import { getCoarseRanges } from '../../properties/utils/coarse-ranges'; import { FormatData } from '../mmcif'; @@ -85,14 +85,14 @@ function getGaussianConformation(data: mmCIF['ihm_gaussian_obj_site']): CoarseGa } function getSegments(asym_id: Column<string>, seq_id_begin: Column<number>, seq_id_end: Column<number>) { - const chainOffsets = [0 as Element]; + const chainOffsets = [0 as ElementIndex]; for (let i = 1, _i = asym_id.rowCount; i < _i; i++) { const newChain = !asym_id.areValuesEqual(i - 1, i); - if (newChain) chainOffsets[chainOffsets.length] = i as Element; + if (newChain) chainOffsets[chainOffsets.length] = i as ElementIndex; } return { - chainSegments: Segmentation.ofOffsets(chainOffsets, Interval.ofBounds(0, asym_id.rowCount)) + chainElementSegments: Segmentation.ofOffsets<ElementIndex, ChainIndex>(chainOffsets, Interval.ofBounds(0, asym_id.rowCount)) } } diff --git a/src/mol-model/structure/model/formats/mmcif/pair-restraints/cross-links.ts b/src/mol-model/structure/model/formats/mmcif/pair-restraints/cross-links.ts index 584a429ccb559cef0e7480f0db2b20734aaa13e0..b16448853b3f3705fe247da037ac9e1d6471e14f 100644 --- a/src/mol-model/structure/model/formats/mmcif/pair-restraints/cross-links.ts +++ b/src/mol-model/structure/model/formats/mmcif/pair-restraints/cross-links.ts @@ -4,21 +4,22 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import Model from '../../../model' +import { Model } from '../../../model' import { Table } from 'mol-data/db' import { mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif'; import { findAtomIndexByLabelName } from '../util'; -import { Element, Unit } from '../../../../structure'; +import { Unit } from '../../../../structure'; +import { ElementIndex } from '../../../indexing'; function findAtomIndex(model: Model, entityId: string, asymId: string, compId: string, seqId: number, atomId: string) { if (!model.atomicHierarchy.atoms.auth_atom_id.isDefined) return -1 const residueIndex = model.atomicHierarchy.findResidueKey(entityId, compId, asymId, seqId, '') if (residueIndex < 0) return -1 - return findAtomIndexByLabelName(model, residueIndex, atomId, '') as Element + return findAtomIndexByLabelName(model, residueIndex, atomId, '') as ElementIndex } export interface IHMCrossLinkRestraint { - getIndicesByElement: (element: Element, kind: Unit.Kind) => number[] + getIndicesByElement: (element: ElementIndex, kind: Unit.Kind) => number[] data: Table<mmCIF_Schema['ihm_cross_link_restraint']> } @@ -47,7 +48,7 @@ export namespace IHMCrossLinkRestraint { atom_id: ihm_cross_link_restraint.atom_id_2, } - function _add(map: Map<Element, number[]>, element: Element, row: number) { + function _add(map: Map<ElementIndex, number[]>, element: ElementIndex, row: number) { const indices = map.get(element) if (indices) indices.push(row) else map.set(element, [ row ]) @@ -60,14 +61,14 @@ export namespace IHMCrossLinkRestraint { if (ihm_cross_link_restraint.model_granularity.value(row) === 'by-atom') { const atomicElement = findAtomIndex(model, entityId, asymId, ps.comp_id.value(row), seqId, ps.atom_id.value(row)) - if (atomicElement >= 0) _add(atomicElementMap, atomicElement as Element, row) + if (atomicElement >= 0) _add(atomicElementMap, atomicElement as ElementIndex, row) } else if (model.coarseHierarchy.isDefined) { const sphereElement = model.coarseHierarchy.spheres.findSequenceKey(entityId, asymId, seqId) if (sphereElement >= 0) { - _add(sphereElementMap, sphereElement as Element, row) + _add(sphereElementMap, sphereElement, row) } else { const gaussianElement = model.coarseHierarchy.gaussians.findSequenceKey(entityId, asymId, seqId) - if (gaussianElement >= 0) _add(gaussianElementMap, gaussianElement as Element, row) + if (gaussianElement >= 0) _add(gaussianElementMap, gaussianElement, row) } } } @@ -81,11 +82,11 @@ export namespace IHMCrossLinkRestraint { } /** map from atomic element to cross link indices */ - const atomicElementMap: Map<Element, number[]> = new Map() + const atomicElementMap: Map<ElementIndex, number[]> = new Map() /** map from sphere element to cross link indices */ - const sphereElementMap: Map<Element, number[]> = new Map() + const sphereElementMap: Map<ElementIndex, number[]> = new Map() /** map from gaussian element to cross link indices */ - const gaussianElementMap: Map<Element, number[]> = new Map() + const gaussianElementMap: Map<ElementIndex, number[]> = new Map() const emptyIndexArray: number[] = []; @@ -95,7 +96,7 @@ export namespace IHMCrossLinkRestraint { } const crossLinkRestraint = { - getIndicesByElement: (element: Element, kind: Unit.Kind) => { + getIndicesByElement: (element: ElementIndex, kind: Unit.Kind) => { const map = getMapByKind(kind) const idx = map.get(element) return idx !== undefined ? idx : emptyIndexArray diff --git a/src/mol-model/structure/model/formats/mmcif/secondary-structure.ts b/src/mol-model/structure/model/formats/mmcif/secondary-structure.ts index 0c045f6052e08bcd1b940eb3b4e940e51fdfdae7..d871052c96890e86bdd7c0b241755a944d2e5b20 100644 --- a/src/mol-model/structure/model/formats/mmcif/secondary-structure.ts +++ b/src/mol-model/structure/model/formats/mmcif/secondary-structure.ts @@ -10,6 +10,7 @@ import { SecondaryStructureType } from '../../types'; import { AtomicHierarchy } from '../../properties/atomic'; import { SecondaryStructure } from '../../properties/seconday-structure'; import { Column } from 'mol-data/db'; +import { ChainIndex, ResidueIndex } from '../../indexing'; export function getSecondaryStructureMmCif(data: mmCIF_Database, hierarchy: AtomicHierarchy): SecondaryStructure { const map: SecondaryStructureMap = new Map(); @@ -37,7 +38,7 @@ type SecondaryStructureEntry = { key: number } type SecondaryStructureMap = Map<string, Map<number, SecondaryStructureEntry>> -type SecondaryStructureData = { type: SecondaryStructureType[], key: number[], elements: SecondaryStructure.Element[] } +type SecondaryStructureData = { type: SecondaryStructureType[], key: number[], elements: SecondaryStructure.Element[] } function addHelices(cat: mmCIF['struct_conf'], map: SecondaryStructureMap, elements: SecondaryStructure.Element[]) { if (!cat._rowCount) return; @@ -47,11 +48,11 @@ function addHelices(cat: mmCIF['struct_conf'], map: SecondaryStructureMap, eleme const { pdbx_PDB_helix_class, conf_type_id, details } = cat; for (let i = 0, _i = cat._rowCount; i < _i; i++) { - const type = SecondaryStructureType.create(pdbx_PDB_helix_class.valueKind(i) === Column.ValueKind.Present + const type = SecondaryStructureType.create(pdbx_PDB_helix_class.valueKind(i) === Column.ValueKind.Present ? SecondaryStructureType.SecondaryStructurePdb[pdbx_PDB_helix_class.value(i)] : conf_type_id.valueKind(i) === Column.ValueKind.Present - ? SecondaryStructureType.SecondaryStructureMmcif[conf_type_id.value(i)] - : SecondaryStructureType.Flag.NA); + ? SecondaryStructureType.SecondaryStructureMmcif[conf_type_id.value(i)] + : SecondaryStructureType.Flag.NA); const element: SecondaryStructure.Helix = { kind: 'helix', @@ -69,6 +70,7 @@ function addHelices(cat: mmCIF['struct_conf'], map: SecondaryStructureMap, eleme key: elements.length }; + elements[elements.length] = element; const asymId = beg_label_asym_id.value(i)!; @@ -129,7 +131,7 @@ function addSheets(cat: mmCIF['struct_sheet_range'], map: SecondaryStructureMap, return; } -function assignSecondaryStructureEntry(hierarchy: AtomicHierarchy, entry: SecondaryStructureEntry, resStart: number, resEnd: number, data: SecondaryStructureData) { +function assignSecondaryStructureEntry(hierarchy: AtomicHierarchy, entry: SecondaryStructureEntry, resStart: ResidueIndex, resEnd: ResidueIndex, data: SecondaryStructureData) { const { label_seq_id, pdbx_PDB_ins_code } = hierarchy.residues; const { endSeqNumber, endInsCode, key, type } = entry; @@ -149,14 +151,13 @@ function assignSecondaryStructureEntry(hierarchy: AtomicHierarchy, entry: Second } function assignSecondaryStructureRanges(hierarchy: AtomicHierarchy, map: SecondaryStructureMap, data: SecondaryStructureData) { - const { segments: chainSegments, count: chainCount } = hierarchy.chainSegments; + const { count: chainCount } = hierarchy.chainAtomSegments; const { label_asym_id } = hierarchy.chains; const { label_seq_id, pdbx_PDB_ins_code } = hierarchy.residues; - for (let cI = 0; cI < chainCount; cI++) { - const resStart = chainSegments[cI], resEnd = chainSegments[cI + 1]; + for (let cI = 0 as ChainIndex; cI < chainCount; cI++) { + const resStart = AtomicHierarchy.chainStartResidueIndex(hierarchy, cI), resEnd = AtomicHierarchy.chainEndResidueIndexExcl(hierarchy, cI); const asymId = label_asym_id.value(cI); - if (map.has(asymId)) { const entries = map.get(asymId)!; diff --git a/src/mol-model/structure/model/formats/mmcif/util.ts b/src/mol-model/structure/model/formats/mmcif/util.ts index 672acf2efc9049c90a361e289088ce1240100c8b..044dee3a6bc4aba02a65e9d047ec0c3e7afe2188 100644 --- a/src/mol-model/structure/model/formats/mmcif/util.ts +++ b/src/mol-model/structure/model/formats/mmcif/util.ts @@ -4,8 +4,8 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import Model from '../../model' -import { Element } from '../../../structure' +import { Model } from '../../model' +import { ElementIndex } from '../../indexing'; export function findEntityIdByAsymId(model: Model, asymId: string) { if (model.sourceData.kind !== 'mmCIF') return '' @@ -16,11 +16,11 @@ export function findEntityIdByAsymId(model: Model, asymId: string) { return '' } -export function findAtomIndexByLabelName(model: Model, residueIndex: number, atomName: string, altLoc: string | null): Element { - const { segments } = model.atomicHierarchy.residueSegments; +export function findAtomIndexByLabelName(model: Model, residueIndex: number, atomName: string, altLoc: string | null): ElementIndex { + const { offsets } = model.atomicHierarchy.residueAtomSegments; const { label_atom_id, label_alt_id } = model.atomicHierarchy.atoms; - for (let i = segments[residueIndex], n = segments[residueIndex + 1]; i < n; ++i) { - if (label_atom_id.value(i) === atomName && (!altLoc || label_alt_id.value(i) === altLoc)) return i as Element; + for (let i = offsets[residueIndex], n = offsets[residueIndex + 1]; i < n; ++i) { + if (label_atom_id.value(i) === atomName && (!altLoc || label_alt_id.value(i) === altLoc)) return i as ElementIndex; } - return -1 as Element; + return -1 as ElementIndex; } \ No newline at end of file diff --git a/src/mol-model/structure/model/indexing.ts b/src/mol-model/structure/model/indexing.ts new file mode 100644 index 0000000000000000000000000000000000000000..65dfce0916e0a663e6f35cb5d89eab9dcea5af53 --- /dev/null +++ b/src/mol-model/structure/model/indexing.ts @@ -0,0 +1,10 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +export type ElementIndex = { readonly '@type': 'element-index' } & number +export type ResidueIndex = { readonly '@type': 'residue-index' } & number +export type ChainIndex = { readonly '@type': 'chain-index' } & number +export type EntityIndex = { readonly '@type': 'entity-index' } & number \ No newline at end of file diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts index 24e5a5875ecb7006e999f998339933509fc97101..03c962fde8304a20953a3d7906173f45d4d32de8 100644 --- a/src/mol-model/structure/model/model.ts +++ b/src/mol-model/structure/model/model.ts @@ -22,7 +22,7 @@ import { ChemicalComponent } from './properties/chemical-component'; * * "Atoms" are integers in the range [0, atomCount). */ -interface Model extends Readonly<{ +export interface Model extends Readonly<{ id: UUID, label: string, @@ -42,7 +42,10 @@ interface Model extends Readonly<{ /** secondary structure provided by the input file */ readonly secondaryStructure: SecondaryStructure, /** maps modified residue name to its parent */ - readonly modifiedResidueNameMap: Map<string, string>, + readonly modifiedResidues: { + parentId: Map<string, string>, + details: Map<string, string> + }, /** maps asym id to unique serial number */ readonly asymIdSerialMap: Map<string, number> /** maps residue name to `ChemicalComponent` data */ @@ -64,25 +67,11 @@ interface Model extends Readonly<{ } { } -namespace Model { +export namespace Model { export function create(format: Format) { switch (format.kind) { // case 'gro': return from_gro(format); case 'mmCIF': return from_mmCIF(format); } } - - // TODO: figure the place to include this? - // export interface Property<T, K> { (model: Model, index: number): T, _kind: K } - // export interface AtomicProperty<T> extends Property<T, 'atomic'> { } - // export interface CoarseProperty<T> extends Property<T, 'coarse'> { } - // export interface SphereProperty<T> extends Property<T, 'sphere'> { } - // export interface GaussianProperty<T> extends Property<T, 'gaussian'> { } - - // export function atomProp<T>(p: (model: Model, i: number) => T): AtomicProperty<T> { return p as any; } - // export function residueProp<T>(p: (model: Model, residueIndex: number) => T): AtomicProperty<T> { - // return p as any; - // } -} - -export default Model \ No newline at end of file +} \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/atomic/hierarchy.ts b/src/mol-model/structure/model/properties/atomic/hierarchy.ts index 8870dd849eac8e355595057099055fc13d65c5d9..33d98c72d09a399e19d76f9810b13f1d5ebc6731 100644 --- a/src/mol-model/structure/model/properties/atomic/hierarchy.ts +++ b/src/mol-model/structure/model/properties/atomic/hierarchy.ts @@ -8,7 +8,7 @@ import { Column, Table } from 'mol-data/db' import { Segmentation } from 'mol-data/int' import { mmCIF_Schema as mmCIF } from 'mol-io/reader/cif/schema/mmcif' import { ElementSymbol } from '../../types' -import { Element } from '../../../structure' +import { ChainIndex, EntityIndex, ResidueIndex, ElementIndex } from '../../indexing'; import SortedRanges from 'mol-data/int/sorted-ranges'; export const AtomsSchema = { @@ -50,34 +50,52 @@ export interface AtomicData { export interface AtomicSegments { /** Maps residueIndex to a range of atoms [segments[rI], segments[rI + 1]) */ - residueSegments: Segmentation<Element>, - /** Maps chainIndex to a range of atoms [segments[cI], segments[cI + 1]) */ - chainSegments: Segmentation<Element>, + residueAtomSegments: Segmentation<ElementIndex, ResidueIndex>, + /** + * Maps chainIndex to a range of atoms [segments[cI], segments[cI + 1]), + * + * residues of i-th chain are accessed like this: + * const rI = residueAtomSegments.index, offsets = chainAtomSegments.offsets; + * const start = rI[offsets[i]], const end = rI[offsets[i + 1] - 1] + 1; + * for (let j = start; j < end; i++) { } + */ + chainAtomSegments: Segmentation<ElementIndex, ChainIndex>, + // TODO: include entity segments? } export interface AtomicKeys { - // TODO: since Atoms must be sorted now, get rid of keys // TODO: include (lazily computed) "entity/chain/residue" indices? - // assign a key to each residue index. - residueKey: ArrayLike<number>, - // assign a key to each chain index - chainKey: ArrayLike<number>, - // assigne a key to each chain index - // also index to the Entities table. - entityKey: ArrayLike<number>, + /** @returns index or -1 if not present. */ + getEntityKey(cI: ChainIndex): EntityIndex, - findChainKey(entityId: string, label_asym_id: string): number, + /** @returns index or -1 if not present. */ + findChainKey(entityId: string, label_asym_id: string): ChainIndex, - /** Unique number for each of the residue. Also the index of the 1st occurence of this residue. */ - findResidueKey(entityId: string, label_asym_id: string, label_comp_id: string, auth_seq_id: number, pdbx_PDB_ins_code: string): number + /** + * Unique number for each of the residue. Also the index of the 1st occurence of this residue. + * @returns index or -1 if not present. + */ + findResidueKey(entityId: string, label_asym_id: string, label_comp_id: string, auth_seq_id: number, pdbx_PDB_ins_code: string): ResidueIndex } export interface AtomicRanges { - polymerRanges: SortedRanges<Element> - gapRanges: SortedRanges<Element> + polymerRanges: SortedRanges<ElementIndex> + gapRanges: SortedRanges<ElementIndex> } type _Hierarchy = AtomicData & AtomicSegments & AtomicKeys & AtomicRanges -export interface AtomicHierarchy extends _Hierarchy { } \ No newline at end of file +export interface AtomicHierarchy extends _Hierarchy { } + +export namespace AtomicHierarchy { + /** Start residue inclusive */ + export function chainStartResidueIndex(segs: AtomicSegments, cI: ChainIndex) { + return segs.residueAtomSegments.index[segs.chainAtomSegments.offsets[cI]]; + } + + /** End residue exclusive */ + export function chainEndResidueIndexExcl(segs: AtomicSegments, cI: ChainIndex) { + return segs.residueAtomSegments.index[segs.chainAtomSegments.offsets[cI + 1] - 1] + 1 as ResidueIndex; + } +} diff --git a/src/mol-model/structure/model/properties/coarse/hierarchy.ts b/src/mol-model/structure/model/properties/coarse/hierarchy.ts index f9057f34894e3604588df1325fa9f1a5ae2795af..ae7eaa26156c11dce3951ad25776e64924156c50 100644 --- a/src/mol-model/structure/model/properties/coarse/hierarchy.ts +++ b/src/mol-model/structure/model/properties/coarse/hierarchy.ts @@ -7,7 +7,7 @@ import { Column } from 'mol-data/db' import { Segmentation } from 'mol-data/int'; -import { Element } from '../../../structure' +import { ElementIndex, ChainIndex } from '../../indexing'; import SortedRanges from 'mol-data/int/sorted-ranges'; export interface CoarsedElementKeys { @@ -17,8 +17,8 @@ export interface CoarsedElementKeys { entityKey: ArrayLike<number>, /** find index of the residue/feature element where seq_id is included */ - findSequenceKey(entityId: string, asym_id: string, seq_id: number): number - findChainKey(entityId: string, asym_id: string): number + findSequenceKey(entityId: string, asym_id: string, seq_id: number): ElementIndex + findChainKey(entityId: string, asym_id: string): ChainIndex } export interface CoarseElementData { @@ -28,12 +28,12 @@ export interface CoarseElementData { seq_id_begin: Column<number>, seq_id_end: Column<number>, - chainSegments: Segmentation<Element>, + chainElementSegments: Segmentation<ElementIndex, ChainIndex>, } export interface CoarseRanges { - polymerRanges: SortedRanges<Element> - gapRanges: SortedRanges<Element> + polymerRanges: SortedRanges<ElementIndex> + gapRanges: SortedRanges<ElementIndex> } export type CoarseElements = CoarsedElementKeys & CoarseElementData & CoarseRanges diff --git a/src/mol-model/structure/model/properties/common.ts b/src/mol-model/structure/model/properties/common.ts index 9dadbc95ac9d951e4d175949655012b0222a52a5..c1b7af14d4e4751d3ddd9c24d535025acbef56fc 100644 --- a/src/mol-model/structure/model/properties/common.ts +++ b/src/mol-model/structure/model/properties/common.ts @@ -5,8 +5,9 @@ */ import { mmCIF_Database as mmCIF } from 'mol-io/reader/cif/schema/mmcif' +import { EntityIndex } from '../indexing'; export interface Entities { data: mmCIF['entity'], - getEntityIndex(id: string): number + getEntityIndex(id: string): EntityIndex } \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/custom/descriptor.ts b/src/mol-model/structure/model/properties/custom/descriptor.ts index 89304d1972e5163eb24b03b44cd925de79a789dd..d86dd94a6fa415db073a466c0493b776854bd575 100644 --- a/src/mol-model/structure/model/properties/custom/descriptor.ts +++ b/src/mol-model/structure/model/properties/custom/descriptor.ts @@ -12,9 +12,7 @@ interface ModelPropertyDescriptor { readonly name: string, cifExport: { - /** used category names that can be used for "filtering" by the writer */ - readonly categoryNames: ReadonlyArray<string>, - categoryProvider: (ctx: CifExportContext) => CifWriter.Category.Provider[] + categories: CifWriter.Category<CifExportContext>[] } } diff --git a/src/mol-model/structure/model/properties/sequence.ts b/src/mol-model/structure/model/properties/sequence.ts index 7725eb561352a4c044b1cd410160e545f31aa8e8..b29ead3f5620b27bc5b4ca9182ee319ae74b8a18 100644 --- a/src/mol-model/structure/model/properties/sequence.ts +++ b/src/mol-model/structure/model/properties/sequence.ts @@ -8,6 +8,7 @@ import { Column } from 'mol-data/db' import { AtomicHierarchy } from './atomic/hierarchy'; import { Entities } from './common'; import { Sequence } from '../../../sequence'; +import { ChainIndex } from '../indexing'; interface StructureSequence { readonly sequences: ReadonlyArray<StructureSequence.Entity>, @@ -25,25 +26,25 @@ namespace StructureSequence { export function fromAtomicHierarchy(entities: Entities, hierarchy: AtomicHierarchy, modResMap?: Map<string, string>): StructureSequence { const { label_comp_id, label_seq_id } = hierarchy.residues - const { chainSegments, residueSegments } = hierarchy + const { chainAtomSegments, residueAtomSegments } = hierarchy const byEntityKey: StructureSequence['byEntityKey'] = { }; const sequences: StructureSequence.Entity[] = []; - for (let cI = 0, _cI = hierarchy.chains._rowCount; cI < _cI; cI++) { - const entityKey = hierarchy.entityKey[cI]; + for (let cI = 0 as ChainIndex, _cI = hierarchy.chains._rowCount; cI < _cI; cI++) { + const entityKey = hierarchy.getEntityKey(cI); // Only for polymers, trying to mirror _entity_poly_seq if (byEntityKey[entityKey] !== void 0 || entities.data.type.value(entityKey) !== 'polymer') continue; let start = cI; cI++; - while (cI < _cI && entityKey === hierarchy.entityKey[cI] && entities.data.type.value(entityKey) !== 'polymer') { + while (cI < _cI && entityKey === hierarchy.getEntityKey(cI) && entities.data.type.value(entityKey) !== 'polymer') { cI++; } cI--; - const rStart = residueSegments.segmentMap[chainSegments.segments[start]]; - const rEnd = residueSegments.segmentMap[chainSegments.segments[cI + 1]]; + const rStart = residueAtomSegments.index[chainAtomSegments.offsets[start]]; + const rEnd = residueAtomSegments.index[chainAtomSegments.offsets[cI + 1]]; const compId = Column.window(label_comp_id, rStart, rEnd); const num = Column.window(label_seq_id, rStart, rEnd); diff --git a/src/mol-model/structure/model/properties/utils/atomic-keys.ts b/src/mol-model/structure/model/properties/utils/atomic-keys.ts index dc4a3ac3f8818c333e59dad03d222e23110bc150..de4029984236b1800a01158a2c37641cd5c8c48d 100644 --- a/src/mol-model/structure/model/properties/utils/atomic-keys.ts +++ b/src/mol-model/structure/model/properties/utils/atomic-keys.ts @@ -7,6 +7,7 @@ import { AtomicData, AtomicSegments, AtomicKeys } from '../atomic' import { Interval, Segmentation } from 'mol-data/int' import { Entities } from '../common' +import { ChainIndex, ResidueIndex, EntityIndex } from '../../indexing'; function getResidueId(comp_id: string, seq_id: number, ins_code: string) { return `${comp_id} ${seq_id} ${ins_code}`; @@ -30,20 +31,20 @@ function createLookUp(entities: Entities, chain: Map<number, Map<string, number> const getEntKey = entities.getEntityIndex; const findChainKey: AtomicKeys['findChainKey'] = (e, c) => { let eKey = getEntKey(e); - if (eKey < 0) return -1; + if (eKey < 0) return -1 as ChainIndex; const cm = chain.get(eKey)!; - if (!cm.has(c)) return -1; - return cm.get(c)!; + if (!cm.has(c)) return -1 as ChainIndex; + return cm.get(c)! as ChainIndex; } const findResidueKey: AtomicKeys['findResidueKey'] = (e, c, name, seq, ins) => { let eKey = getEntKey(e); - if (eKey < 0) return -1; + if (eKey < 0) return -1 as ResidueIndex; const cm = chain.get(eKey)!; - if (!cm.has(c)) return -1; + if (!cm.has(c)) return -1 as ResidueIndex; const rm = residue.get(cm.get(c)!)! const id = getResidueId(name, seq, ins); - if (!rm.has(id)) return -1; - return rm.get(id)!; + if (!rm.has(id)) return -1 as ResidueIndex; + return rm.get(id)! as ResidueIndex; } return { findChainKey, findResidueKey }; } @@ -67,7 +68,7 @@ export function getAtomicKeys(data: AtomicData, entities: Entities, segments: At const atomSet = Interval.ofBounds(0, data.atoms._rowCount); - const chainsIt = Segmentation.transientSegments(segments.chainSegments, atomSet); + const chainsIt = Segmentation.transientSegments(segments.chainAtomSegments, atomSet); while (chainsIt.hasNext) { const chainSegment = chainsIt.move(); const cI = chainSegment.index; @@ -81,7 +82,7 @@ export function getAtomicKeys(data: AtomicData, entities: Entities, segments: At entityKey[cI] = eKey; const residueMap = getElementSubstructureKeyMap(residueMaps, cKey); - const residuesIt = Segmentation.transientSegments(segments.residueSegments, atomSet, chainSegment); + const residuesIt = Segmentation.transientSegments(segments.residueAtomSegments, atomSet, chainSegment); while (residuesIt.hasNext) { const residueSegment = residuesIt.move(); const rI = residueSegment.index; @@ -92,5 +93,5 @@ export function getAtomicKeys(data: AtomicData, entities: Entities, segments: At const { findChainKey, findResidueKey } = createLookUp(entities, chainMaps, residueMaps); - return { residueKey, chainKey, entityKey, findChainKey, findResidueKey }; + return { getEntityKey: cI => entityKey[cI] as EntityIndex, findChainKey, findResidueKey }; } \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/utils/atomic-ranges.ts b/src/mol-model/structure/model/properties/utils/atomic-ranges.ts index a28eca281de545c9079cbea9618b19f7dbbdf9ae..73d7053736c092b95bbbdc279870b8b86864d789 100644 --- a/src/mol-model/structure/model/properties/utils/atomic-ranges.ts +++ b/src/mol-model/structure/model/properties/utils/atomic-ranges.ts @@ -8,15 +8,15 @@ import { AtomicSegments } from '../atomic'; import { AtomicData, AtomicRanges } from '../atomic/hierarchy'; import { Segmentation, Interval } from 'mol-data/int'; import SortedRanges from 'mol-data/int/sorted-ranges'; -import { Element } from '../../../../structure'; import { ChemicalComponent } from '../chemical-component'; import { MoleculeType, isPolymer } from '../../types'; +import { ElementIndex } from '../../indexing'; export function getAtomicRanges(data: AtomicData, segments: AtomicSegments, chemicalComponentMap: Map<string, ChemicalComponent>): AtomicRanges { const polymerRanges: number[] = [] const gapRanges: number[] = [] - const chainIt = Segmentation.transientSegments(segments.chainSegments, Interval.ofBounds(0, data.atoms._rowCount)) - const residueIt = Segmentation.transientSegments(segments.residueSegments, Interval.ofBounds(0, data.atoms._rowCount)) + const chainIt = Segmentation.transientSegments(segments.chainAtomSegments, Interval.ofBounds(0, data.atoms._rowCount)) + const residueIt = Segmentation.transientSegments(segments.residueAtomSegments, Interval.ofBounds(0, data.atoms._rowCount)) const { label_seq_id, label_comp_id } = data.residues let prevSeqId: number @@ -63,7 +63,7 @@ export function getAtomicRanges(data: AtomicData, segments: AtomicSegments, chem console.log(polymerRanges, gapRanges) return { - polymerRanges: SortedRanges.ofSortedRanges(polymerRanges as Element[]), - gapRanges: SortedRanges.ofSortedRanges(gapRanges as Element[]) + polymerRanges: SortedRanges.ofSortedRanges(polymerRanges as ElementIndex[]), + gapRanges: SortedRanges.ofSortedRanges(gapRanges as ElementIndex[]) } } \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/utils/coarse-keys.ts b/src/mol-model/structure/model/properties/utils/coarse-keys.ts index dfc035c16ddc5303fad22752138ca09c3a1e5438..b4fc8a60b6716ba3952e06007376dbddc57428f7 100644 --- a/src/mol-model/structure/model/properties/utils/coarse-keys.ts +++ b/src/mol-model/structure/model/properties/utils/coarse-keys.ts @@ -7,6 +7,7 @@ import { Entities } from '../common'; import { CoarseElementData, CoarsedElementKeys } from '../coarse'; +import { ChainIndex, ElementIndex } from '../../indexing'; function getElementKey(map: Map<string, number>, key: string, counter: { index: number }) { if (map.has(key)) return map.get(key)!; @@ -26,22 +27,22 @@ function createLookUp(entities: Entities, chain: Map<number, Map<string, number> const getEntKey = entities.getEntityIndex; const findChainKey: CoarsedElementKeys['findChainKey'] = (e, c) => { const eKey = getEntKey(e); - if (eKey < 0) return -1; + if (eKey < 0) return -1 as ChainIndex; const cm = chain.get(eKey)!; - if (!cm.has(c)) return -1; - return cm.get(c)!; + if (!cm.has(c)) return -1 as ChainIndex; + return cm.get(c)! as ChainIndex; } // TODO consider implementing as binary search const findSequenceKey: CoarsedElementKeys['findSequenceKey'] = (e, c, s) => { const eKey = getEntKey(e); - if (eKey < 0) return -1; + if (eKey < 0) return -1 as ElementIndex; const cm = chain.get(eKey); - if (cm === undefined) return -1 + if (cm === undefined) return -1 as ElementIndex const cKey = cm.get(c) - if (cKey === undefined) return -1 + if (cKey === undefined) return -1 as ElementIndex const sm = seq.get(cKey)! - if (!sm.has(s)) return -1; - return sm.get(s)! + if (!sm.has(s)) return -1 as ElementIndex; + return sm.get(s)! as ElementIndex } return { findChainKey, findSequenceKey }; } @@ -51,7 +52,7 @@ function missingEntity(k: string) { } export function getCoarseKeys(data: CoarseElementData, entities: Entities): CoarsedElementKeys { - const { entity_id, asym_id, seq_id_begin, seq_id_end, count, chainSegments } = data; + const { entity_id, asym_id, seq_id_begin, seq_id_end, count, chainElementSegments } = data; const seqMaps = new Map<number, Map<number, number>>(); const chainMaps = new Map<number, Map<string, number>>(), chainCounter = { index: 0 }; @@ -64,8 +65,8 @@ export function getCoarseKeys(data: CoarseElementData, entities: Entities): Coar if (entityKey[i] < 0) missingEntity(entity_id.value(i)); } - for (let cI = 0; cI < chainSegments.count; cI++) { - const start = chainSegments.segments[cI], end = chainSegments.segments[cI + 1]; + for (let cI = 0; cI < chainElementSegments.count; cI++) { + const start = chainElementSegments.offsets[cI], end = chainElementSegments.offsets[cI + 1]; const map = getElementSubstructureKeyMap(chainMaps, entityKey[start]); const key = getElementKey(map, asym_id.value(start), chainCounter); for (let i = start; i < end; i++) chainKey[i] = key; diff --git a/src/mol-model/structure/model/properties/utils/coarse-ranges.ts b/src/mol-model/structure/model/properties/utils/coarse-ranges.ts index 635bc51847a5fbc6681ab6019e6016f988d605ee..4e3cc0101cab5990dfd7a30bd027e6d00421a2c4 100644 --- a/src/mol-model/structure/model/properties/utils/coarse-ranges.ts +++ b/src/mol-model/structure/model/properties/utils/coarse-ranges.ts @@ -7,15 +7,15 @@ import { CoarseRanges, CoarseElementData } from '../coarse/hierarchy'; import { Segmentation, Interval } from 'mol-data/int'; import SortedRanges from 'mol-data/int/sorted-ranges'; -import { Element } from '../../../../structure'; import { ChemicalComponent } from '../chemical-component'; +import { ElementIndex } from '../../indexing'; // TODO assumes all coarse elements are part of a polymer export function getCoarseRanges(data: CoarseElementData, chemicalComponentMap: Map<string, ChemicalComponent>): CoarseRanges { const polymerRanges: number[] = [] const gapRanges: number[] = [] - const chainIt = Segmentation.transientSegments(data.chainSegments, Interval.ofBounds(0, data.count)) + const chainIt = Segmentation.transientSegments(data.chainElementSegments, Interval.ofBounds(0, data.count)) const { seq_id_begin, seq_id_end } = data @@ -46,7 +46,7 @@ export function getCoarseRanges(data: CoarseElementData, chemicalComponentMap: M console.log(polymerRanges, gapRanges) return { - polymerRanges: SortedRanges.ofSortedRanges(polymerRanges as Element[]), - gapRanges: SortedRanges.ofSortedRanges(gapRanges as Element[]) + polymerRanges: SortedRanges.ofSortedRanges(polymerRanges as ElementIndex[]), + gapRanges: SortedRanges.ofSortedRanges(gapRanges as ElementIndex[]) } } \ No newline at end of file diff --git a/src/mol-model/structure/query/generators.ts b/src/mol-model/structure/query/generators.ts index 290a8434117072f3439ab327cc212948021e1369..9000205da2eb9c4aaa1c77141abdde5adecba0ac 100644 --- a/src/mol-model/structure/query/generators.ts +++ b/src/mol-model/structure/query/generators.ts @@ -6,22 +6,22 @@ import Query from './query' import Selection from './selection' -import { Element, Unit, StructureProperties as P } from '../structure' -import { OrderedSet, Segmentation } from 'mol-data/int' +import { StructureElement, Unit, StructureProperties as P } from '../structure' +import { Segmentation } from 'mol-data/int' import { LinearGroupingBuilder } from './utils/builders'; export const all: Query.Provider = async (s, ctx) => Selection.Singletons(s, s); export interface AtomQueryParams { - entityTest: Element.Predicate, - chainTest: Element.Predicate, - residueTest: Element.Predicate, - atomTest: Element.Predicate, - groupBy: Element.Property<any> + entityTest: StructureElement.Predicate, + chainTest: StructureElement.Predicate, + residueTest: StructureElement.Predicate, + atomTest: StructureElement.Predicate, + groupBy: StructureElement.Property<any> } export interface AtomGroupsQueryParams extends AtomQueryParams { - groupBy: Element.Property<any> + groupBy: StructureElement.Property<any> } export function residues(params?: Partial<AtomQueryParams>) { return atoms({ ...params, groupBy: P.residue.key }); } @@ -43,10 +43,10 @@ export function atoms(params?: Partial<AtomGroupsQueryParams>): Query.Provider { return atomGroupsGrouped(normalized); } -function atomGroupsLinear(atomTest: Element.Predicate): Query.Provider { +function atomGroupsLinear(atomTest: StructureElement.Predicate): Query.Provider { return async (structure, ctx) => { const { units } = structure; - const l = Element.Location(); + const l = StructureElement.create(); const builder = structure.subsetBuilder(true); let progress = 0; @@ -72,7 +72,7 @@ function atomGroupsLinear(atomTest: Element.Predicate): Query.Provider { function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: AtomGroupsQueryParams): Query.Provider { return async (structure, ctx) => { const { units } = structure; - const l = Element.Location(); + const l = StructureElement.create(); const builder = structure.subsetBuilder(true); let progress = 0; @@ -83,24 +83,24 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A const elements = unit.elements; builder.beginUnit(unit.id); - const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainSegments, elements); - const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueSegments, elements); + const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, elements); + const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, elements); while (chainsIt.hasNext) { const chainSegment = chainsIt.move(); - l.element = OrderedSet.getAt(elements, chainSegment.start); + l.element = elements[chainSegment.start]; // test entity and chain if (!entityTest(l) || !chainTest(l)) continue; residuesIt.setSegment(chainSegment); while (residuesIt.hasNext) { const residueSegment = residuesIt.move(); - l.element = OrderedSet.getAt(elements, residueSegment.start); + l.element = elements[residueSegment.start]; // test residue if (!residueTest(l)) continue; for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) { - l.element = OrderedSet.getAt(elements, j); + l.element = elements[j]; if (atomTest(l)) { builder.addElement(l.element); } @@ -120,7 +120,7 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, groupBy }: AtomGroupsQueryParams): Query.Provider { return async (structure, ctx) => { const { units } = structure; - const l = Element.Location(); + const l = StructureElement.create(); const builder = new LinearGroupingBuilder(structure); let progress = 0; @@ -130,24 +130,24 @@ function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, group l.unit = unit; const elements = unit.elements; - const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainSegments, elements); - const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueSegments, elements); + const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, elements); + const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, elements); while (chainsIt.hasNext) { const chainSegment = chainsIt.move(); - l.element = OrderedSet.getAt(elements, chainSegment.start); + l.element = elements[chainSegment.start]; // test entity and chain if (!entityTest(l) || !chainTest(l)) continue; residuesIt.setSegment(chainSegment); while (residuesIt.hasNext) { const residueSegment = residuesIt.move(); - l.element = OrderedSet.getAt(elements, residueSegment.start); + l.element = elements[residueSegment.start]; // test residue if (!residueTest(l)) continue; for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) { - l.element = OrderedSet.getAt(elements, j); + l.element = elements[j]; if (atomTest(l)) builder.add(groupBy(l), unit.id, l.element); } } diff --git a/src/mol-model/structure/query/modifiers.ts b/src/mol-model/structure/query/modifiers.ts index 3b332ff7fcfc9d142ae080968375a3c54af0eedb..01150fa2ff3c4aaf828dc151d88671dd4a7039ba 100644 --- a/src/mol-model/structure/query/modifiers.ts +++ b/src/mol-model/structure/query/modifiers.ts @@ -21,14 +21,14 @@ function getWholeResidues(ctx: RuntimeContext, source: Structure, structure: Str continue; } - const { residueSegments } = unit.model.atomicHierarchy; + const { residueAtomSegments } = unit.model.atomicHierarchy; const elements = unit.elements; builder.beginUnit(unit.id); - const residuesIt = Segmentation.transientSegments(residueSegments, elements); + const residuesIt = Segmentation.transientSegments(residueAtomSegments, elements); while (residuesIt.hasNext) { const rI = residuesIt.move().index; - for (let j = residueSegments.segments[rI], _j = residueSegments.segments[rI + 1]; j < _j; j++) { + for (let j = residueAtomSegments.offsets[rI], _j = residueAtomSegments.offsets[rI + 1]; j < _j; j++) { builder.addElement(j); } } diff --git a/src/mol-model/structure/query/predicates.ts b/src/mol-model/structure/query/predicates.ts index 99ef9538902702dcc4435009327cb42dba429995..c1b14849e65994aeb0b4273c8fc5416fe1951f21 100644 --- a/src/mol-model/structure/query/predicates.ts +++ b/src/mol-model/structure/query/predicates.ts @@ -4,19 +4,19 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Element, StructureProperties as P } from '../structure' +import { StructureElement, StructureProperties as P } from '../structure' namespace Predicates { export interface SetLike<A> { has(v: A): boolean } function isSetLike<A>(x: any): x is SetLike<A> { return !!x && !!x.has } - export function eq<A>(p: Element.Property<A>, value: A): Element.Predicate { return l => p(l) === value; } - export function lt<A>(p: Element.Property<A>, value: A): Element.Predicate { return l => p(l) < value; } - export function lte<A>(p: Element.Property<A>, value: A): Element.Predicate { return l => p(l) <= value; } - export function gt<A>(p: Element.Property<A>, value: A): Element.Predicate { return l => p(l) > value; } - export function gte<A>(p: Element.Property<A>, value: A): Element.Predicate { return l => p(l) >= value; } + export function eq<A>(p: StructureElement.Property<A>, value: A): StructureElement.Predicate { return l => p(l) === value; } + export function lt<A>(p: StructureElement.Property<A>, value: A): StructureElement.Predicate { return l => p(l) < value; } + export function lte<A>(p: StructureElement.Property<A>, value: A): StructureElement.Predicate { return l => p(l) <= value; } + export function gt<A>(p: StructureElement.Property<A>, value: A): StructureElement.Predicate { return l => p(l) > value; } + export function gte<A>(p: StructureElement.Property<A>, value: A): StructureElement.Predicate { return l => p(l) >= value; } - export function inSet<A>(p: Element.Property<A>, values: SetLike<A> | ArrayLike<A>): Element.Predicate { + export function inSet<A>(p: StructureElement.Property<A>, values: SetLike<A> | ArrayLike<A>): StructureElement.Predicate { if (isSetLike(values)) { return l => values.has(p(l)); } else { @@ -27,7 +27,7 @@ namespace Predicates { } } - export function and(...ps: Element.Predicate[]): Element.Predicate { + export function and(...ps: StructureElement.Predicate[]): StructureElement.Predicate { switch (ps.length) { case 0: return P.constant.true; case 1: return ps[0]; @@ -61,7 +61,7 @@ namespace Predicates { } } - export function or(...ps: Element.Predicate[]): Element.Predicate { + export function or(...ps: StructureElement.Predicate[]): StructureElement.Predicate { switch (ps.length) { case 0: return P.constant.false; case 1: return ps[0]; diff --git a/src/mol-model/structure/query/selection.ts b/src/mol-model/structure/query/selection.ts index d554a3bf46ef0401cdda250a77d6a4449ef0c14f..72ab29106a7b81a30cef2ebcac96c8a83ebb17ac 100644 --- a/src/mol-model/structure/query/selection.ts +++ b/src/mol-model/structure/query/selection.ts @@ -5,7 +5,7 @@ */ import { HashSet } from 'mol-data/generic' -import { Structure, Element, Unit } from '../structure' +import { Structure, StructureElement, Unit } from '../structure' import { structureUnion } from './utils/structure'; import { OrderedSet, SortedArray } from 'mol-data/int'; @@ -35,13 +35,13 @@ namespace Selection { return structureUnion(sel.source, sel.structures); } - export function toLoci(sel: Selection): Element.Loci { - const loci: { unit: Unit, indices: OrderedSet<Element.Index> }[] = []; + export function toLoci(sel: Selection): StructureElement.Loci { + const loci: { unit: Unit, indices: OrderedSet<StructureElement.UnitIndex> }[] = []; const { unitMap } = sel.source; for (const unit of unionStructure(sel).units) { if (unit === unitMap.get(unit.id)) { - loci[loci.length] = { unit, indices: OrderedSet.ofBounds(0 as Element.Index, unit.elements.length as Element.Index) }; + loci[loci.length] = { unit, indices: OrderedSet.ofBounds(0 as StructureElement.UnitIndex, unit.elements.length as StructureElement.UnitIndex) }; } else { loci[loci.length] = { unit, @@ -50,7 +50,7 @@ namespace Selection { } } - return Element.Loci(loci); + return StructureElement.Loci(loci); } export interface Builder { diff --git a/src/mol-model/structure/query/utils/builders.ts b/src/mol-model/structure/query/utils/builders.ts index 0abce36d09e40a367f5a8cbb36589f6fd31d1667..b47a265bd7ffa55aff54cc6150a5c5bac0992b90 100644 --- a/src/mol-model/structure/query/utils/builders.ts +++ b/src/mol-model/structure/query/utils/builders.ts @@ -4,11 +4,12 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Element, Structure } from '../../structure'; +import { StructureElement, Structure } from '../../structure'; import Selection from '../selection'; import { HashSet } from 'mol-data/generic'; import { structureUnion } from './structure'; import { StructureSubsetBuilder } from '../../structure/util/subset-builder'; +import { ElementIndex } from '../../model'; export class UniqueStructuresBuilder { private set = HashSet(Structure.hashCode, Structure.areEqual); @@ -36,7 +37,7 @@ export class LinearGroupingBuilder { private builders: StructureSubsetBuilder[] = []; private builderMap = new Map<string, StructureSubsetBuilder>(); - add(key: any, unit: number, element: Element) { + add(key: any, unit: number, element: ElementIndex) { let b = this.builderMap.get(key); if (!b) { b = this.source.subsetBuilder(true); @@ -55,7 +56,7 @@ export class LinearGroupingBuilder { private singletonSelection(): Selection { const builder = this.source.subsetBuilder(true); - const loc = Element.Location(); + const loc = StructureElement.create(); for (let i = 0, _i = this.builders.length; i < _i; i++) { this.builders[i].setSingletonLocation(loc); builder.addToUnit(loc.unit.id, loc.element); diff --git a/src/mol-model/structure/query/utils/structure.ts b/src/mol-model/structure/query/utils/structure.ts index c1e9424397c539405f830840ccf496674c2ee416..85867419505a8e3494e2f7a9e70838f140c0a00c 100644 --- a/src/mol-model/structure/query/utils/structure.ts +++ b/src/mol-model/structure/query/utils/structure.ts @@ -4,7 +4,7 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Structure, Unit, Element } from '../../structure' +import { Structure, Unit, StructureElement } from '../../structure' import { SortedArray } from 'mol-data/int'; import { StructureSubsetBuilder } from '../../structure/util/subset-builder'; @@ -12,7 +12,7 @@ export function structureUnion(source: Structure, structures: Structure[]) { if (structures.length === 0) return Structure.Empty; if (structures.length === 1) return structures[0]; - const unitMap = new Map<number, Element.Set>(); + const unitMap = new Map<number, StructureElement.Set>(); const fullUnits = new Set<number>(); for (const { units } of structures) { @@ -36,7 +36,7 @@ export function structureUnion(source: Structure, structures: Structure[]) { return builder.getStructure(); } -function buildUnion(this: StructureSubsetBuilder, elements: Element.Set, id: number) { +function buildUnion(this: StructureSubsetBuilder, elements: StructureElement.Set, id: number) { this.setUnit(id, elements); } diff --git a/src/mol-model/structure/structure.ts b/src/mol-model/structure/structure.ts index e642278518140bb5e51cc26e31f11245e12a57da..3a924335ea86458fea0e1d2cb89d2be92310bdee 100644 --- a/src/mol-model/structure/structure.ts +++ b/src/mol-model/structure/structure.ts @@ -4,11 +4,11 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import Element from './structure/element' +import StructureElement from './structure/element' import Structure from './structure/structure' import Unit from './structure/unit' import StructureSymmetry from './structure/symmetry' import { Link } from './structure/unit/links' import StructureProperties from './structure/properties' -export { Element, Link, Structure, Unit, StructureSymmetry, StructureProperties } \ No newline at end of file +export { StructureElement, Link, Structure, Unit, StructureSymmetry, StructureProperties } \ No newline at end of file diff --git a/src/mol-model/structure/structure/element.ts b/src/mol-model/structure/structure/element.ts index 819d17687ebacafb5e48a0fd4abf885876b9d9a1..f8e35caff577b023e4d64ef5c19abf1822ac3bc3 100644 --- a/src/mol-model/structure/structure/element.ts +++ b/src/mol-model/structure/structure/element.ts @@ -6,35 +6,35 @@ import { OrderedSet, SortedArray } from 'mol-data/int' import Unit from './unit' +import { ElementIndex } from '../model'; -/** Element index in Model */ -type Element = { readonly '@type': 'element' } & number +interface StructureElement<U = Unit> { + unit: U, + /** Index into element (atomic/coarse) properties of unit.model */ + element: ElementIndex +} + +namespace StructureElement { + export function create(unit?: Unit, element?: ElementIndex): StructureElement { return { unit: unit as any, element: element || (0 as ElementIndex) }; } -namespace Element { - export type Set = SortedArray<Element> + // TODO: when nominal types are available, make this indexed by UnitIndex + export type Set = SortedArray<ElementIndex> /** Index into Unit.elements */ - export type Index = { readonly '@type': 'element-index' } & number + export type UnitIndex = { readonly '@type': 'structure-element-index' } & number - /** All the information required to access element properties */ - export interface Location<U = Unit> { - unit: U, - /** Index into element (atomic/coarse) properties of unit.model */ - element: Element - } - export function Location(unit?: Unit, element?: Element): Location { return { unit: unit as any, element: element || (0 as Element) }; } - export interface Property<T> { (location: Location): T } + export interface Property<T> { (location: StructureElement): T } export interface Predicate extends Property<boolean> { } export function property<T>(p: Property<T>) { return p; } function _wrongUnitKind(kind: string) { throw new Error(`Property only available for ${kind} models.`); } - export function atomicProperty<T>(p: (location: Location<Unit.Atomic>) => T) { - return property(l => Unit.isAtomic(l.unit) ? p(l as Location<Unit.Atomic>) : _wrongUnitKind('atomic') ); + export function atomicProperty<T>(p: (location: StructureElement<Unit.Atomic>) => T) { + return property(l => Unit.isAtomic(l.unit) ? p(l as StructureElement<Unit.Atomic>) : _wrongUnitKind('atomic') ); } - export function coarseProperty<T>(p: (location: Location<Unit.Spheres | Unit.Gaussians>) => T) { - return property(l => Unit.isCoarse(l.unit) ? p(l as Location<Unit.Spheres | Unit.Gaussians>) : _wrongUnitKind('coarse') ); + export function coarseProperty<T>(p: (location: StructureElement<Unit.Spheres | Unit.Gaussians>) => T) { + return property(l => Unit.isCoarse(l.unit) ? p(l as StructureElement<Unit.Spheres | Unit.Gaussians>) : _wrongUnitKind('coarse') ); } /** Represents multiple element index locations */ @@ -47,11 +47,11 @@ namespace Element { * Indices into the unit.elements array. * Can use OrderedSet.forEach to iterate (or OrderedSet.size + OrderedSet.getAt) */ - indices: OrderedSet<Index> + indices: OrderedSet<UnitIndex> }> } - export function Loci(elements: ArrayLike<{ unit: Unit, indices: OrderedSet<Index> }>): Loci { + export function Loci(elements: ArrayLike<{ unit: Unit, indices: OrderedSet<UnitIndex> }>): Loci { return { kind: 'element-loci', elements: elements as Loci['elements'] }; } @@ -60,4 +60,4 @@ namespace Element { } } -export default Element \ No newline at end of file +export default StructureElement \ No newline at end of file diff --git a/src/mol-model/structure/structure/properties.ts b/src/mol-model/structure/structure/properties.ts index d13f5ddd88a4de7f85ee503e589925e244e0d621..bc95808367257eaa14cdc9fcd4403e527880f39a 100644 --- a/src/mol-model/structure/structure/properties.ts +++ b/src/mol-model/structure/structure/properties.ts @@ -4,14 +4,14 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import Element from './element' +import StructureElement from './element' import Unit from './unit' import { VdwRadius } from '../model/properties/atomic'; const constant = { - true: Element.property(l => true), - false: Element.property(l => false), - zero: Element.property(l => 0) + true: StructureElement.property(l => true), + false: StructureElement.property(l => false), + zero: StructureElement.property(l => 0) } function notAtomic(): never { @@ -26,73 +26,73 @@ function notCoarse(kind?: string): never { // TODO: remove the type checks? const atom = { - key: Element.property(l => l.element), + key: StructureElement.property(l => l.element), // Conformation - x: Element.property(l => l.unit.conformation.x(l.element)), - y: Element.property(l => l.unit.conformation.y(l.element)), - z: Element.property(l => l.unit.conformation.z(l.element)), - id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicConformation.atomId.value(l.element)), - occupancy: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicConformation.occupancy.value(l.element)), - B_iso_or_equiv: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicConformation.B_iso_or_equiv.value(l.element)), + x: StructureElement.property(l => l.unit.conformation.x(l.element)), + y: StructureElement.property(l => l.unit.conformation.y(l.element)), + z: StructureElement.property(l => l.unit.conformation.z(l.element)), + id: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicConformation.atomId.value(l.element)), + occupancy: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicConformation.occupancy.value(l.element)), + B_iso_or_equiv: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicConformation.B_iso_or_equiv.value(l.element)), // Hierarchy - type_symbol: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.type_symbol.value(l.element)), - label_atom_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.label_atom_id.value(l.element)), - auth_atom_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.auth_atom_id.value(l.element)), - label_alt_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.label_alt_id.value(l.element)), - pdbx_formal_charge: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.pdbx_formal_charge.value(l.element)), + type_symbol: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.type_symbol.value(l.element)), + label_atom_id: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.label_atom_id.value(l.element)), + auth_atom_id: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.auth_atom_id.value(l.element)), + label_alt_id: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.label_alt_id.value(l.element)), + pdbx_formal_charge: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.pdbx_formal_charge.value(l.element)), // Derived - vdw_radius: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : VdwRadius(l.unit.model.atomicHierarchy.atoms.type_symbol.value(l.element))), + vdw_radius: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : VdwRadius(l.unit.model.atomicHierarchy.atoms.type_symbol.value(l.element))), } const residue = { - key: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residueKey[l.unit.residueIndex[l.element]]), + key: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.residueIndex[l.element]), - group_PDB: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.group_PDB.value(l.unit.residueIndex[l.element])), - label_comp_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.label_comp_id.value(l.unit.residueIndex[l.element])), - auth_comp_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.auth_comp_id.value(l.unit.residueIndex[l.element])), - label_seq_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.label_seq_id.value(l.unit.residueIndex[l.element])), - auth_seq_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.element])), - pdbx_PDB_ins_code: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.pdbx_PDB_ins_code.value(l.unit.residueIndex[l.element])), + group_PDB: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.group_PDB.value(l.unit.residueIndex[l.element])), + label_comp_id: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.label_comp_id.value(l.unit.residueIndex[l.element])), + auth_comp_id: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.auth_comp_id.value(l.unit.residueIndex[l.element])), + label_seq_id: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.label_seq_id.value(l.unit.residueIndex[l.element])), + auth_seq_id: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.element])), + pdbx_PDB_ins_code: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.pdbx_PDB_ins_code.value(l.unit.residueIndex[l.element])), // Properties - secondary_structure_type: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.properties.secondaryStructure.type[l.unit.residueIndex[l.element]]), - secondary_structure_key: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.properties.secondaryStructure.key[l.unit.residueIndex[l.element]]), + secondary_structure_type: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.properties.secondaryStructure.type[l.unit.residueIndex[l.element]]), + secondary_structure_key: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.properties.secondaryStructure.key[l.unit.residueIndex[l.element]]), } const chain = { - key: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chainKey[l.unit.chainIndex[l.element]]), + key: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.chainIndex[l.element]), - label_asym_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chains.label_asym_id.value(l.unit.chainIndex[l.element])), - auth_asym_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chains.auth_asym_id.value(l.unit.chainIndex[l.element])), - label_entity_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chains.label_entity_id.value(l.unit.chainIndex[l.element])) + label_asym_id: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chains.label_asym_id.value(l.unit.chainIndex[l.element])), + auth_asym_id: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chains.auth_asym_id.value(l.unit.chainIndex[l.element])), + label_entity_id: StructureElement.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chains.label_entity_id.value(l.unit.chainIndex[l.element])) } const coarse = { key: atom.key, - entityKey: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.entityKey[l.element]), + entityKey: StructureElement.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.entityKey[l.element]), x: atom.x, y: atom.y, z: atom.z, - asym_id: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.asym_id.value(l.element)), - seq_id_begin: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.seq_id_begin.value(l.element)), - seq_id_end: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.seq_id_end.value(l.element)), + asym_id: StructureElement.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.asym_id.value(l.element)), + seq_id_begin: StructureElement.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.seq_id_begin.value(l.element)), + seq_id_end: StructureElement.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.seq_id_end.value(l.element)), - sphere_radius: Element.property(l => !Unit.isSpheres(l.unit) ? notCoarse('spheres') : l.unit.coarseConformation.radius[l.element]), - sphere_rmsf: Element.property(l => !Unit.isSpheres(l.unit) ? notCoarse('spheres') : l.unit.coarseConformation.rmsf[l.element]), + sphere_radius: StructureElement.property(l => !Unit.isSpheres(l.unit) ? notCoarse('spheres') : l.unit.coarseConformation.radius[l.element]), + sphere_rmsf: StructureElement.property(l => !Unit.isSpheres(l.unit) ? notCoarse('spheres') : l.unit.coarseConformation.rmsf[l.element]), - gaussian_weight: Element.property(l => !Unit.isGaussians(l.unit) ? notCoarse('gaussians') : l.unit.coarseConformation.weight[l.element]), - gaussian_covariance_matrix: Element.property(l => !Unit.isGaussians(l.unit) ? notCoarse('gaussians') : l.unit.coarseConformation.covariance_matrix[l.element]) + gaussian_weight: StructureElement.property(l => !Unit.isGaussians(l.unit) ? notCoarse('gaussians') : l.unit.coarseConformation.weight[l.element]), + gaussian_covariance_matrix: StructureElement.property(l => !Unit.isGaussians(l.unit) ? notCoarse('gaussians') : l.unit.coarseConformation.covariance_matrix[l.element]) } -function eK(l: Element.Location) { +function eK(l: StructureElement) { switch (l.unit.kind) { case Unit.Kind.Atomic: - return l.unit.model.atomicHierarchy.entityKey[l.unit.chainIndex[l.element]] + return l.unit.model.atomicHierarchy.getEntityKey(l.unit.chainIndex[l.element]) case Unit.Kind.Spheres: return l.unit.model.coarseHierarchy.spheres.entityKey[l.element] case Unit.Kind.Gaussians: @@ -103,21 +103,21 @@ function eK(l: Element.Location) { const entity = { key: eK, - id: Element.property(l => l.unit.model.entities.data.id.value(eK(l))), - type: Element.property(l => l.unit.model.entities.data.type.value(eK(l))), - src_method: Element.property(l => l.unit.model.entities.data.src_method.value(eK(l))), - pdbx_description: Element.property(l => l.unit.model.entities.data.pdbx_description.value(eK(l))), - formula_weight: Element.property(l => l.unit.model.entities.data.formula_weight.value(eK(l))), - pdbx_number_of_molecules: Element.property(l => l.unit.model.entities.data.pdbx_number_of_molecules.value(eK(l))), - details: Element.property(l => l.unit.model.entities.data.details.value(eK(l))), - pdbx_mutation: Element.property(l => l.unit.model.entities.data.pdbx_mutation.value(eK(l))), - pdbx_fragment: Element.property(l => l.unit.model.entities.data.pdbx_fragment.value(eK(l))), - pdbx_ec: Element.property(l => l.unit.model.entities.data.pdbx_ec.value(eK(l))) + id: StructureElement.property(l => l.unit.model.entities.data.id.value(eK(l))), + type: StructureElement.property(l => l.unit.model.entities.data.type.value(eK(l))), + src_method: StructureElement.property(l => l.unit.model.entities.data.src_method.value(eK(l))), + pdbx_description: StructureElement.property(l => l.unit.model.entities.data.pdbx_description.value(eK(l))), + formula_weight: StructureElement.property(l => l.unit.model.entities.data.formula_weight.value(eK(l))), + pdbx_number_of_molecules: StructureElement.property(l => l.unit.model.entities.data.pdbx_number_of_molecules.value(eK(l))), + details: StructureElement.property(l => l.unit.model.entities.data.details.value(eK(l))), + pdbx_mutation: StructureElement.property(l => l.unit.model.entities.data.pdbx_mutation.value(eK(l))), + pdbx_fragment: StructureElement.property(l => l.unit.model.entities.data.pdbx_fragment.value(eK(l))), + pdbx_ec: StructureElement.property(l => l.unit.model.entities.data.pdbx_ec.value(eK(l))) } const unit = { - operator_name: Element.property(l => l.unit.conformation.operator.name), - model_num: Element.property(l => l.unit.model.modelNum) + operator_name: StructureElement.property(l => l.unit.conformation.operator.name), + model_num: StructureElement.property(l => l.unit.model.modelNum) } const StructureProperties = { diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts index 7202df6b7937c805104ed526a5c63e393c7d1c8f..1e806d8d8ec5e1514815a62c322478a6db2924c4 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -7,9 +7,9 @@ import { IntMap, SortedArray, Iterator, Segmentation } from 'mol-data/int' import { UniqueArray } from 'mol-data/generic' import { SymmetryOperator } from 'mol-math/geometry/symmetry-operator' -import { Model } from '../model' +import { Model, ElementIndex } from '../model' import { sort, arraySwap, hash1, sortArray } from 'mol-data/util'; -import Element from './element' +import StructureElement from './element' import Unit from './unit' import { StructureLookup3D } from './util/lookup3d'; import { CoarseElements } from '../model/properties/coarse'; @@ -49,7 +49,7 @@ class Structure { return hash; } - elementLocations(): Iterator<Element.Location> { + elementLocations(): Iterator<StructureElement> { return new Structure.ElementLocationIterator(this); } @@ -118,20 +118,20 @@ namespace Structure { * of consecutive "single atom chains". */ export function ofModel(model: Model): Structure { - const chains = model.atomicHierarchy.chainSegments; + const chains = model.atomicHierarchy.chainAtomSegments; const builder = new StructureBuilder(); for (let c = 0; c < chains.count; c++) { - const start = chains.segments[c]; + const start = chains.offsets[c]; // merge all consecutive "single atom chains" while (c + 1 < chains.count - && chains.segments[c + 1] - chains.segments[c] === 1 - && chains.segments[c + 2] - chains.segments[c + 1] === 1) { + && chains.offsets[c + 1] - chains.offsets[c] === 1 + && chains.offsets[c + 2] - chains.offsets[c + 1] === 1) { c++; } - const elements = SortedArray.ofBounds(start as Element, chains.segments[c + 1] as Element); + const elements = SortedArray.ofBounds(start as ElementIndex, chains.offsets[c + 1] as ElementIndex); builder.addUnit(Unit.Kind.Atomic, model, SymmetryOperator.Default, elements); } @@ -149,9 +149,9 @@ namespace Structure { } function addCoarseUnits(builder: StructureBuilder, model: Model, elements: CoarseElements, kind: Unit.Kind) { - const { chainSegments } = elements; - for (let cI = 0; cI < chainSegments.count; cI++) { - const elements = SortedArray.ofBounds(chainSegments.segments[cI] as Element, chainSegments.segments[cI + 1] as Element); + const { chainElementSegments } = elements; + for (let cI = 0; cI < chainElementSegments.count; cI++) { + const elements = SortedArray.ofBounds<ElementIndex>(chainElementSegments.offsets[cI], chainElementSegments.offsets[cI + 1]); builder.addUnit(kind, model, SymmetryOperator.Default, elements); } } @@ -159,7 +159,7 @@ namespace Structure { export class StructureBuilder { private units: Unit[] = []; - addUnit(kind: Unit.Kind, model: Model, operator: SymmetryOperator, elements: Element.Set): Unit { + addUnit(kind: Unit.Kind, model: Model, operator: SymmetryOperator, elements: StructureElement.Set): Unit { const unit = Unit.create(this.units.length, kind, model, operator, elements); this.units.push(unit); return unit; @@ -211,15 +211,15 @@ namespace Structure { return true; } - export class ElementLocationIterator implements Iterator<Element.Location> { - private current = Element.Location(); + export class ElementLocationIterator implements Iterator<StructureElement> { + private current = StructureElement.create(); private unitIndex = 0; - private elements: Element.Set; + private elements: StructureElement.Set; private maxIdx = 0; private idx = -1; hasNext: boolean; - move(): Element.Location { + move(): StructureElement { this.advance(); this.current.element = this.elements[this.idx]; return this.current; @@ -257,7 +257,7 @@ namespace Structure { export function getEntityKeys(structure: Structure) { const { units } = structure; - const l = Element.Location(); + const l = StructureElement.create(); const keys = UniqueArray.create<number, number>(); for (const unit of units) { @@ -266,7 +266,7 @@ namespace Structure { l.unit = unit; const elements = unit.elements; - const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainSegments, elements); + const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, elements); while (chainsIt.hasNext) { const chainSegment = chainsIt.move(); l.element = elements[chainSegment.start]; diff --git a/src/mol-model/structure/structure/unit.ts b/src/mol-model/structure/structure/unit.ts index 92d0da4976ae61b274077657d485a18ce7e40c0d..48f429a29dc65e7f1d883669d7ea6d112635a57a 100644 --- a/src/mol-model/structure/structure/unit.ts +++ b/src/mol-model/structure/structure/unit.ts @@ -12,7 +12,8 @@ import { IntraUnitLinks, computeIntraUnitBonds } from './unit/links' import { CoarseElements, CoarseSphereConformation, CoarseGaussianConformation } from '../model/properties/coarse'; import { ValueRef } from 'mol-util'; import { UnitRings } from './unit/rings'; -import Element from './element' +import StructureElement from './element' +import { ChainIndex, ResidueIndex } from '../model/indexing'; // A building block of a structure that corresponds to an atomic or a coarse grained representation // 'conveniently grouped together'. @@ -26,7 +27,7 @@ namespace Unit { export function isSpheres(u: Unit): u is Spheres { return u.kind === Kind.Spheres; } export function isGaussians(u: Unit): u is Gaussians { return u.kind === Kind.Gaussians; } - export function create(id: number, kind: Kind, model: Model, operator: SymmetryOperator, elements: Element.Set): Unit { + export function create(id: number, kind: Kind, model: Model, operator: SymmetryOperator, elements: StructureElement.Set): Unit { switch (kind) { case Kind.Atomic: return new Atomic(id, unitIdFactory(), model, elements, SymmetryOperator.createMapping(operator, model.atomicConformation), AtomicProperties()); case Kind.Spheres: return createCoarse(id, unitIdFactory(), model, Kind.Spheres, elements, SymmetryOperator.createMapping(operator, model.coarseConformation.spheres)); @@ -36,7 +37,7 @@ namespace Unit { /** A group of units that differ only by symmetry operators. */ export type SymmetryGroup = { - readonly elements: Element.Set, + readonly elements: StructureElement.Set, readonly units: ReadonlyArray<Unit> readonly hashCode: number } @@ -53,11 +54,11 @@ namespace Unit { readonly id: number, // invariant ID stays the same even if the Operator/conformation changes. readonly invariantId: number, - readonly elements: Element.Set, + readonly elements: StructureElement.Set, readonly model: Model, readonly conformation: SymmetryOperator.ArrayMapping, - getChild(elements: Element.Set): Unit, + getChild(elements: StructureElement.Set): Unit, applyOperator(id: number, operator: SymmetryOperator, dontCompose?: boolean /* = false */): Unit, readonly lookup3d: Lookup3D @@ -77,17 +78,17 @@ namespace Unit { readonly id: number; readonly invariantId: number; - readonly elements: Element.Set; + readonly elements: StructureElement.Set; readonly model: Model; readonly conformation: SymmetryOperator.ArrayMapping; // Reference some commonly accessed things for faster access. - readonly residueIndex: ArrayLike<number>; - readonly chainIndex: ArrayLike<number>; + readonly residueIndex: ArrayLike<ResidueIndex>; + readonly chainIndex: ArrayLike<ChainIndex>; private props: AtomicProperties; - getChild(elements: Element.Set): Unit { + getChild(elements: StructureElement.Set): Unit { if (elements.length === this.elements.length) return this; return new Atomic(this.id, this.invariantId, this.model, elements, this.conformation, AtomicProperties()); } @@ -116,15 +117,15 @@ namespace Unit { return this.props.rings.ref; } - constructor(id: number, invariantId: number, model: Model, elements: Element.Set, conformation: SymmetryOperator.ArrayMapping, props: AtomicProperties) { + constructor(id: number, invariantId: number, model: Model, elements: StructureElement.Set, conformation: SymmetryOperator.ArrayMapping, props: AtomicProperties) { this.id = id; this.invariantId = invariantId; this.model = model; this.elements = elements; this.conformation = conformation; - this.residueIndex = model.atomicHierarchy.residueSegments.segmentMap; - this.chainIndex = model.atomicHierarchy.chainSegments.segmentMap; + this.residueIndex = model.atomicHierarchy.residueAtomSegments.index; + this.chainIndex = model.atomicHierarchy.chainAtomSegments.index; this.props = props; } } @@ -144,14 +145,14 @@ namespace Unit { readonly id: number; readonly invariantId: number; - readonly elements: Element.Set; + readonly elements: StructureElement.Set; readonly model: Model; readonly conformation: SymmetryOperator.ArrayMapping; readonly coarseElements: CoarseElements; readonly coarseConformation: C; - getChild(elements: Element.Set): Unit { + getChild(elements: StructureElement.Set): Unit { if (elements.length === this.elements.length) return this as any as Unit /** lets call this an ugly temporary hack */; return createCoarse(this.id, this.invariantId, this.model, this.kind, elements, this.conformation); } @@ -176,7 +177,7 @@ namespace Unit { return this.kind === Kind.Spheres ? this.model.coarseConformation.spheres : this.model.coarseConformation.gaussians; } - constructor(id: number, invariantId: number, model: Model, kind: K, elements: Element.Set, conformation: SymmetryOperator.ArrayMapping) { + constructor(id: number, invariantId: number, model: Model, kind: K, elements: StructureElement.Set, conformation: SymmetryOperator.ArrayMapping) { this.kind = kind; this.id = id; this.invariantId = invariantId; @@ -188,7 +189,7 @@ namespace Unit { } } - function createCoarse<K extends Kind.Gaussians | Kind.Spheres>(id: number, invariantId: number, model: Model, kind: K, elements: Element.Set, conformation: SymmetryOperator.ArrayMapping): Unit { + function createCoarse<K extends Kind.Gaussians | Kind.Spheres>(id: number, invariantId: number, model: Model, kind: K, elements: StructureElement.Set, conformation: SymmetryOperator.ArrayMapping): Unit { return new Coarse(id, invariantId, model, kind, elements, conformation) as any as Unit /** lets call this an ugly temporary hack */; } diff --git a/src/mol-model/structure/structure/unit/rings/compute.ts b/src/mol-model/structure/structure/unit/rings/compute.ts index c0bafe3e3abe380d3557ef14edfdd3225299902b..69ffa0506d0499e7870ff19010edf55d95d45405 100644 --- a/src/mol-model/structure/structure/unit/rings/compute.ts +++ b/src/mol-model/structure/structure/unit/rings/compute.ts @@ -13,7 +13,7 @@ export default function computeRings(unit: Unit.Atomic) { const size = largestResidue(unit); const state = State(unit, size); - const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueSegments, unit.elements); + const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements); while (residuesIt.hasNext) { const seg = residuesIt.move(); processResidue(state, seg.start, seg.end); @@ -75,7 +75,7 @@ function resetState(state: State) { } function largestResidue(unit: Unit.Atomic) { - const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueSegments, unit.elements); + const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements); let size = 0; while (residuesIt.hasNext) { const seg = residuesIt.move(); diff --git a/src/mol-model/structure/structure/util/subset-builder.ts b/src/mol-model/structure/structure/util/subset-builder.ts index 96732ebb5ec238baae6839ef54feeee3f8777826..77c228902fc14c27a75e070df1868498ca68057a 100644 --- a/src/mol-model/structure/structure/util/subset-builder.ts +++ b/src/mol-model/structure/structure/util/subset-builder.ts @@ -6,19 +6,20 @@ import { IntMap, SortedArray } from 'mol-data/int'; import { sortArray } from 'mol-data/util'; -import Element from '../element'; +import StructureElement from '../element'; import StructureSymmetry from '../symmetry'; import Unit from '../unit'; import Structure from '../structure'; +import { ElementIndex } from '../../model'; export class StructureSubsetBuilder { private ids: number[] = []; - private unitMap = IntMap.Mutable<Element[]>(); + private unitMap = IntMap.Mutable<ElementIndex[]>(); private parentId = -1; - private currentUnit: Element[] = []; + private currentUnit: ElementIndex[] = []; elementCount = 0; - addToUnit(parentId: number, e: Element) { + addToUnit(parentId: number, e: ElementIndex) { const unit = this.unitMap.get(parentId); if (!!unit) { unit[unit.length] = e; } else { @@ -33,7 +34,7 @@ export class StructureSubsetBuilder { this.currentUnit = this.currentUnit.length > 0 ? [] : this.currentUnit; } - addElement(e: Element) { + addElement(e: ElementIndex) { this.currentUnit[this.currentUnit.length] = e; this.elementCount++; } @@ -45,9 +46,9 @@ export class StructureSubsetBuilder { this.parentId = -1; } - setUnit(parentId: number, elements: ArrayLike<Element>) { + setUnit(parentId: number, elements: ArrayLike<ElementIndex>) { this.ids[this.ids.length] = parentId; - this.unitMap.set(parentId, elements as Element[]); + this.unitMap.set(parentId, elements as ElementIndex[]); this.elementCount += elements.length; } @@ -100,7 +101,7 @@ export class StructureSubsetBuilder { return this._getStructure(true); } - setSingletonLocation(location: Element.Location) { + setSingletonLocation(location: StructureElement) { const id = this.ids[0]; location.unit = this.parent.unitMap.get(id); location.element = this.unitMap.get(id)[0]; diff --git a/src/mol-view/label.ts b/src/mol-view/label.ts index e2c54c34f644e3e5b0f989ee837e165510fc453e..288eae4673297a388605118adc133dfe5ae1830f 100644 --- a/src/mol-view/label.ts +++ b/src/mol-view/label.ts @@ -5,14 +5,14 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Unit, Element, StructureProperties as Props } from 'mol-model/structure'; +import { Unit, StructureElement, StructureProperties as Props } from 'mol-model/structure'; import { Loci } from 'mol-model/loci'; import { OrderedSet } from 'mol-data/int'; -const elementLocA = Element.Location() -const elementLocB = Element.Location() +const elementLocA = StructureElement.create() +const elementLocB = StructureElement.create() -function setElementLocation(loc: Element.Location, unit: Unit, index: number) { +function setElementLocation(loc: StructureElement, unit: Unit, index: number) { loc.unit = unit loc.element = unit.elements[index] } @@ -23,7 +23,7 @@ export function labelFirst(loci: Loci): string { const e = loci.elements[0] if (e) { const el = e.unit.elements[OrderedSet.getAt(e.indices, 0)]; - return elementLabel(Element.Location(e.unit, el)) + return elementLabel(StructureElement.create(e.unit, el)) } else { return 'Unknown' } @@ -43,7 +43,7 @@ export function labelFirst(loci: Loci): string { } } -export function elementLabel(loc: Element.Location) { +export function elementLabel(loc: StructureElement) { const model = loc.unit.model.label const instance = loc.unit.conformation.operator.name let element = '' diff --git a/src/perf-tests/cif-encoder.ts b/src/perf-tests/cif-encoder.ts index 2d07b1890fc145d7f5d185057d19d21bc94227a5..f8f2ab18c9bb925ae7ba1b46dd5c83b1ccd564ef 100644 --- a/src/perf-tests/cif-encoder.ts +++ b/src/perf-tests/cif-encoder.ts @@ -5,6 +5,7 @@ */ import { CifWriter } from 'mol-io/writer/cif' +import * as fs from 'fs' const category1fields: CifWriter.Field[] = [ CifWriter.Field.str('f1', i => 'v' + i), @@ -18,24 +19,49 @@ const category2fields: CifWriter.Field[] = [ CifWriter.Field.float('e3', i => Math.random()), ]; -function getInstance(ctx: { name: string, fields: CifWriter.Field[], rowCount: number }): CifWriter.Category { +function getCat(name: string): CifWriter.Category { return { - data: void 0, - name: ctx.name, - fields: ctx.fields, - rowCount: ctx.rowCount + name, + instance(ctx: { fields: CifWriter.Field[], rowCount: number }) { + return { data: void 0, fields: ctx.fields, rowCount: ctx.rowCount }; + } + }; +} + +function testText() { + const enc = CifWriter.createEncoder(); + + const filter: CifWriter.Category.Filter = { + includeCategory(cat) { return true; }, + includeField(cat, field) { return !(cat === 'cat2' && field === 'e2') } } + + enc.startDataBlock('test'); + enc.setFilter(filter); + enc.writeCategory(getCat('cat1'), [{ rowCount: 5, fields: category1fields }]); + enc.writeCategory(getCat('cat2'), [{ rowCount: 1, fields: category2fields }]); + console.log(enc.getData()); } -const enc = CifWriter.createEncoder(); +testText(); + + +function testBinary() { + const enc = CifWriter.createEncoder({ binary: true }); + + const filter: CifWriter.Category.Filter = { + includeCategory(cat) { return true; }, + includeField(cat, field) { return !(cat === 'cat2' && field === 'e2') } + } -const filter: CifWriter.Category.Filter = { - includeCategory(cat) { return true; }, - includeField(cat, field) { return !(cat === 'cat2' && field === 'e2') } + enc.startDataBlock('test'); + enc.setFilter(filter); + enc.writeCategory(getCat('cat1'), [{ rowCount: 5, fields: category1fields }]); + enc.writeCategory(getCat('cat2'), [{ rowCount: 1, fields: category2fields }]); + enc.encode(); + const data = enc.getData() as Uint8Array; + fs.writeFileSync('e:/test/mol-star/test.bcif', new Buffer(data)); + console.log('written binary'); } -enc.startDataBlock('test'); -enc.setFilter(filter); -enc.writeCategory(getInstance, [{ rowCount: 5, name: 'cat1', fields: category1fields }]); -enc.writeCategory(getInstance, [{ rowCount: 1, name: 'cat2', fields: category2fields }]); -console.log(enc.getData()); +testBinary(); \ No newline at end of file diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts index ee8cae29c8f7465af1b6c26a340df099a41747b0..90566d25f7202a16ad1ef7c6bb458297f41b3d29 100644 --- a/src/perf-tests/structure.ts +++ b/src/perf-tests/structure.ts @@ -11,7 +11,7 @@ import * as fs from 'fs' import fetch from 'node-fetch' import CIF from 'mol-io/reader/cif' -import { Structure, Model, Queries as Q, Element, Selection, StructureSymmetry, Query, Format, StructureProperties as SP } from 'mol-model/structure' +import { Structure, Model, Queries as Q, StructureElement, Selection, StructureSymmetry, Query, Format, StructureProperties as SP } from 'mol-model/structure' //import { Segmentation, OrderedSet } from 'mol-data/int' import to_mmCIF from 'mol-model/structure/export/mmcif' @@ -119,8 +119,8 @@ export namespace PropertyAccess { return s; } - function sumProperty(structure: Structure, p: Element.Property<number>) { - const l = Element.Location(); + function sumProperty(structure: Structure, p: StructureElement.Property<number>) { + const l = StructureElement.create(); let s = 0; for (const unit of structure.units) { diff --git a/src/servers/model/server/api.ts b/src/servers/model/server/api.ts index 88830f6cd93311f75a0f404ed42315e3259e6223..0e60d47074d27b278086f6eb3015906ae65153b9 100644 --- a/src/servers/model/server/api.ts +++ b/src/servers/model/server/api.ts @@ -4,7 +4,7 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Query, Queries, Structure, Element, StructureSymmetry, StructureProperties as Props } from 'mol-model/structure'; +import { Query, Queries, Structure, StructureElement, StructureSymmetry, StructureProperties as Props } from 'mol-model/structure'; export enum QueryParamType { String, @@ -51,26 +51,26 @@ const AtomSiteParameters = { // return Element.property(l => p(l) === id); // } -function entityTest1_555(params: any): Element.Predicate | undefined { - if (typeof params.entity_id === 'undefined') return Element.property(l => l.unit.conformation.operator.isIdentity); +function entityTest1_555(params: any): StructureElement.Predicate | undefined { + if (typeof params.entity_id === 'undefined') return StructureElement.property(l => l.unit.conformation.operator.isIdentity); const p = Props.entity.id, id = '' + params.entityId; - return Element.property(l => l.unit.conformation.operator.isIdentity && p(l) === id); + return StructureElement.property(l => l.unit.conformation.operator.isIdentity && p(l) === id); } -function chainTest(params: any): Element.Predicate | undefined { +function chainTest(params: any): StructureElement.Predicate | undefined { if (typeof params.label_asym_id !== 'undefined') { const p = Props.chain.label_asym_id, id = '' + params.label_asym_id; - return Element.property(l => p(l) === id); + return StructureElement.property(l => p(l) === id); } if (typeof params.auth_asym_id !== 'undefined') { const p = Props.chain.auth_asym_id, id = '' + params.auth_asym_id; - return Element.property(l => p(l) === id); + return StructureElement.property(l => p(l) === id); } return void 0; } -function residueTest(params: any): Element.Predicate | undefined { - const props: Element.Property<any>[] = [], values: any[] = []; +function residueTest(params: any): StructureElement.Predicate | undefined { + const props: StructureElement.Property<any>[] = [], values: any[] = []; if (typeof params.label_seq_id !== 'undefined') { props.push(Props.residue.label_seq_id); @@ -99,12 +99,12 @@ function residueTest(params: any): Element.Predicate | undefined { switch (props.length) { case 0: return void 0; - case 1: return Element.property(l => props[0](l) === values[0]); - case 2: return Element.property(l => props[0](l) === values[0] && props[1](l) === values[1]); - case 3: return Element.property(l => props[0](l) === values[0] && props[1](l) === values[1] && props[2](l) === values[2]); + case 1: return StructureElement.property(l => props[0](l) === values[0]); + case 2: return StructureElement.property(l => props[0](l) === values[0] && props[1](l) === values[1]); + case 3: return StructureElement.property(l => props[0](l) === values[0] && props[1](l) === values[1] && props[2](l) === values[2]); default: { const len = props.length; - return Element.property(l => { + return StructureElement.property(l => { for (let i = 0; i < len; i++) if (!props[i](l) !== values[i]) return false; return true; }); diff --git a/src/servers/model/server/query.ts b/src/servers/model/server/query.ts index ed9ebedf88f1901edca307a812b4df045fada995..2dc635c4ee79bdeaa7735533615cd7a49a7e2f5d 100644 --- a/src/servers/model/server/query.ts +++ b/src/servers/model/server/query.ts @@ -95,6 +95,7 @@ export async function resolveRequest(req: Request, writer: Writer) { }; encoder.writeCategory(_model_server_stats, [stats]); + encoder.encode(); encoder.writeTo(writer); @@ -112,9 +113,9 @@ import CifField = CifWriter.Field function string<T>(name: string, str: (data: T, i: number) => string, isSpecified?: (data: T) => boolean): CifField<number, T> { if (isSpecified) { - return CifField.str(name, (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 CifField.str(name, (i, d) => str(d, i)); + return CifField.str(name, (i, d) => str(d, i)); } function int32<T>(name: string, value: (data: T) => number): CifField<number, T> { @@ -144,33 +145,29 @@ const _model_server_stats_fields: CifField<number, Stats>[] = [ ]; -function _model_server_result(request: Request): CifWriter.Category { - return { - data: request, - name: 'model_server_result', - fields: _model_server_result_fields, - rowCount: 1 - }; -} - -function _model_server_params(request: Request): CifWriter.Category { - const params: string[][] = []; - for (const k of Object.keys(request.normalizedParams)) { - params.push([k, '' + request.normalizedParams[k]]); +const _model_server_result: CifWriter.Category<Request> = { + name: 'model_server_result', + instance: (request) => ({ data: request, fields: _model_server_result_fields, rowCount: 1 }) +}; + +const _model_server_params: CifWriter.Category<Request> = { + name: 'model_server_params', + instance(request) { + const params: string[][] = []; + for (const k of Object.keys(request.normalizedParams)) { + params.push([k, '' + request.normalizedParams[k]]); + } + return { + data: params, + + fields: _model_server_params_fields, + rowCount: params.length + } } - return { - data: params, - name: 'model_server_params', - fields: _model_server_params_fields, - rowCount: params.length - }; -} +}; -function _model_server_stats(stats: Stats): CifWriter.Category { - return { - data: stats, - name: 'model_server_stats', - fields: _model_server_stats_fields, - rowCount: 1 - }; + +const _model_server_stats: CifWriter.Category<Stats> = { + name: 'model_server_stats', + instance: (stats) => ({ data: 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 c0cbab224c18bb56fca276cdd4412caab18bafb2..a754ef489fbe3d5eea0c1ff1869f7c52c38d4eec 100644 --- a/src/servers/volume/server/query/encode.ts +++ b/src/servers/volume/server/query/encode.ts @@ -28,9 +28,9 @@ interface ResultContext { function string<T>(name: string, str: (data: T) => string, isSpecified?: (data: T) => boolean): CifWriter.Field<number, T> { if (isSpecified) { - return CifWriter.Field.str(name, (i, d) => str(d), { valueKind: (i, d) => isSpecified(d) ? Column.ValueKind.Present : Column.ValueKind.NotPresent }); + return CifWriter.Field.str(name, (i, d) => str(d), { valueKind: (i, d) => isSpecified(d) ? Column.ValueKind.Present : Column.ValueKind.NotPresent }); } - return CifWriter.Field.str(name, (i, d) => str(d)); + return CifWriter.Field.str(name, (i, d) => str(d)); } function int32<T>(name: string, value: (data: T) => number): CifWriter.Field<number, T> { @@ -90,53 +90,54 @@ const _volume_data_3d_info_fields = [ float64<_vd3d_Ctx>('max_sampled', ctx => ctx.sampledValuesInfo.max) ]; -function _volume_data_3d_info(result: ResultContext): CifWriter.Category { - const ctx: _vd3d_Ctx = { - header: result.query.data.header, - channelIndex: result.channelIndex, - grid: result.query.samplingInfo.gridDomain, - sampleRate: result.query.samplingInfo.sampling.rate, - globalValuesInfo: result.query.data.header.sampling[0].valuesInfo[result.channelIndex], - sampledValuesInfo: result.query.data.header.sampling[result.query.samplingInfo.sampling.index].valuesInfo[result.channelIndex] - }; - - return { - data: ctx, - name: 'volume_data_3d_info', - fields: _volume_data_3d_info_fields, - rowCount: 1 - }; -} +const _volume_data_3d_info: CifWriter.Category<ResultContext> = { + name: 'volume_data_3d_info', + instance(result) { + const ctx: _vd3d_Ctx = { + header: result.query.data.header, + channelIndex: result.channelIndex, + grid: result.query.samplingInfo.gridDomain, + sampleRate: result.query.samplingInfo.sampling.rate, + globalValuesInfo: result.query.data.header.sampling[0].valuesInfo[result.channelIndex], + sampledValuesInfo: result.query.data.header.sampling[result.query.samplingInfo.sampling.index].valuesInfo[result.channelIndex] + }; + + return { data: ctx, fields: _volume_data_3d_info_fields, rowCount: 1 } + } +}; function _volume_data_3d_number(i: number, ctx: DataFormat.ValueArray): number { return ctx[i]; } -function _volume_data_3d(ctx: ResultContext) { - const data = ctx.query.values[ctx.channelIndex]; - - const E = ArrayEncoding; - let encoder: ArrayEncoder; - let typedArray: any; - if (ctx.query.data.header.valueType === DataFormat.ValueType.Float32 || ctx.query.data.header.valueType === DataFormat.ValueType.Int16) { - let min: number, max: number; - min = data[0], max = data[0]; - for (let i = 0, n = data.length; i < n; i++) { - let v = data[i]; - if (v < min) min = v; - else if (v > max) max = v; +const _volume_data_3d: CifWriter.Category<ResultContext> = { + name: 'volume_data_3d', + instance(ctx) { + const data = ctx.query.values[ctx.channelIndex]; + + const E = ArrayEncoding; + let encoder: ArrayEncoder; + let typedArray: any; + if (ctx.query.data.header.valueType === DataFormat.ValueType.Float32 || ctx.query.data.header.valueType === DataFormat.ValueType.Int16) { + let min: number, max: number; + min = data[0], max = data[0]; + for (let i = 0, n = data.length; i < n; i++) { + let v = data[i]; + if (v < min) min = v; + else if (v > max) max = v; + } + typedArray = Float32Array; + // encode into 255 steps and store each value in 1 byte. + encoder = E.by(E.intervalQuantizaiton(min, max, 255, Uint8Array)).and(E.byteArray); + } else { + typedArray = Int8Array; + // just encode the bytes + encoder = E.by(E.byteArray) } - typedArray = Float32Array; - // encode into 255 steps and store each value in 1 byte. - encoder = E.by(E.intervalQuantizaiton(min, max, 255, Uint8Array)).and(E.byteArray); - } else { - typedArray = Int8Array; - // just encode the bytes - encoder = E.by(E.byteArray) - } - const fields = [CifWriter.Field.float('values', _volume_data_3d_number, { encoder, typedArray, digitCount: 6 })]; - return { data, name: 'volume_data_3d', fields, rowCount: data.length }; + const fields = [CifWriter.Field.float('values', _volume_data_3d_number, { encoder, typedArray, digitCount: 6 })]; + return { data, fields, rowCount: data.length }; + } } function pickQueryBoxDimension(ctx: Data.QueryContext, e: 'a' | 'b', d: number) { @@ -171,13 +172,9 @@ const _density_server_result_fields = [ queryBoxDimension('b', 2) ] -function _density_server_result(ctx: Data.QueryContext): CifWriter.Category { - return { - data: ctx, - name: 'density_server_result', - fields: _density_server_result_fields, - rowCount: 1 - }; +const _density_server_result: CifWriter.Category<Data.QueryContext> = { + name: 'density_server_result', + instance: ctx => ({ data: ctx, fields: _density_server_result_fields, rowCount: 1 }) } function write(encoder: CifWriter.Encoder, query: Data.QueryContext) {