Newer
Older
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { ResidueIndex, ChainIndex, ElementIndex, EntityIndex } from '../../indexing';
import { Unit, Structure, StructureElement } from '../../../structure';
import { Segmentation } from 'mol-data/int';
import { UUID } from 'mol-util';
import { CifWriter } from 'mol-io/writer/cif';
import { Model } from '../../model';
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
export interface IndexedCustomProperty<Idx extends IndexedCustomProperty.Index, T = any> {
readonly id: UUID,
readonly kind: Unit.Kind,
readonly level: IndexedCustomProperty.Level,
has(idx: Idx): boolean,
get(idx: Idx): T | undefined,
getExportContext(structure: Structure): IndexedCustomProperty.ExportCtx<T>
}
export namespace IndexedCustomProperty {
export type Index = ElementIndex | ResidueIndex | ChainIndex | EntityIndex
export type Level = 'atom' | 'residue' | 'chain' | 'entity'
export interface ExportCtx<T> {
elements: StructureElement[],
property(index: number): T
}
export function getCifDataSource<Idx extends Index, T>(structure: Structure, prop: IndexedCustomProperty<Idx, T> | undefined, cache: any): CifWriter.Category.Instance['source'][0] {
if (!prop) return { rowCount: 0 };
if (cache && cache[prop.id]) return cache[prop.id];
const data = prop.getExportContext(structure);
const ret = { data, rowCount: data.elements.length };
if (cache) cache[prop.id] = ret;
return ret;
}
export type Atom<T> = IndexedCustomProperty<ElementIndex, T>
export function fromAtomMap<T>(map: Map<ElementIndex, T>): Atom<T> {
return new ElementMappedCustomProperty(map);
}
export function fromAtomArray<T>(array: ArrayLike<T>): Atom<T> {
// TODO: create "array based custom property" as optimization
return new ElementMappedCustomProperty(arrayToMap(array));
}
export type Residue<T> = IndexedCustomProperty<ResidueIndex, T>
const getResidueSegments = (model: Model) => model.atomicHierarchy.residueAtomSegments;
export function fromResidueMap<T>(map: Map<ResidueIndex, T>): Residue<T> {
return new SegmentedMappedIndexedCustomProperty('residue', map, getResidueSegments, Unit.Kind.Atomic);
}
export function fromResidueArray<T>(array: ArrayLike<T>): Residue<T> {
// TODO: create "array based custom property" as optimization
return new SegmentedMappedIndexedCustomProperty('residue', arrayToMap(array), getResidueSegments, Unit.Kind.Atomic);
}
export type Chain<T> = IndexedCustomProperty<ChainIndex, T>
const getChainSegments = (model: Model) => model.atomicHierarchy.chainAtomSegments;
export function fromChainMap<T>(map: Map<ChainIndex, T>): Chain<T> {
return new SegmentedMappedIndexedCustomProperty('chain', map, getChainSegments, Unit.Kind.Atomic);
}
export function fromChainArray<T>(array: ArrayLike<T>): Chain<T> {
// TODO: create "array based custom property" as optimization
return new SegmentedMappedIndexedCustomProperty('chain', arrayToMap(array), getChainSegments, Unit.Kind.Atomic);
}
export type Entity<T> = IndexedCustomProperty<EntityIndex, T>
export function fromEntityMap<T>(map: Map<EntityIndex, T>): Entity<T> {
return new EntityMappedCustomProperty(map);
}
}
function arrayToMap<Idx extends IndexedCustomProperty.Index, T>(array: ArrayLike<T>): Map<Idx, T> {
const ret = new Map<Idx, T>();
for (let i = 0 as Idx, _i = array.length; i < _i; i++) ret.set(i, array[i]);
return ret;
}
class SegmentedMappedIndexedCustomProperty<Idx extends IndexedCustomProperty.Index, T = any> implements IndexedCustomProperty<Idx, T> {
readonly id: UUID = UUID.create();
readonly kind: Unit.Kind;
has(idx: Idx): boolean { return this.map.has(idx); }
get(idx: Idx) { return this.map.get(idx); }
private getStructureElements(structure: Structure) {
const models = structure.models;
if (models.length !== 1) throw new Error(`Only works on structures with a single model.`);
const seenIndices = new Set<Idx>();
const unitGroups = structure.unitSymmetryGroups;
const loci: StructureElement[] = [];
const segments = this.segmentGetter(models[0]);
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
for (const unitGroup of unitGroups) {
const unit = unitGroup.units[0];
if (unit.kind !== this.kind) {
continue;
}
const chains = Segmentation.transientSegments(segments, unit.elements);
while (chains.hasNext) {
const seg = chains.move();
if (!this.has(seg.index) || seenIndices.has(seg.index)) continue;
seenIndices.add(seg.index);
loci[loci.length] = StructureElement.create(unit, unit.elements[seg.start]);
}
}
loci.sort((x, y) => x.element - y.element);
return loci;
}
getExportContext(structure: Structure): IndexedCustomProperty.ExportCtx<T> {
const index = this.segmentGetter(structure.model).index;
const elements = this.getStructureElements(structure);
return { elements, property: i => this.get(index[elements[i].element])! };
}
constructor(public level: 'residue' | 'chain', private map: Map<Idx, T>, private segmentGetter: (model: Model) => Segmentation<ElementIndex, Idx>, kind: Unit.Kind) {
class ElementMappedCustomProperty<T = any> implements IndexedCustomProperty<ElementIndex, T> {
readonly id: UUID = UUID.create();
readonly kind: Unit.Kind;
readonly level = 'atom';
has(idx: ElementIndex): boolean { return this.map.has(idx); }
get(idx: ElementIndex) { return this.map.get(idx); }
private getStructureElements(structure: Structure) {
const models = structure.models;
if (models.length !== 1) throw new Error(`Only works on structures with a single model.`);
const seenIndices = new Set<ElementIndex>();
const unitGroups = structure.unitSymmetryGroups;
const loci: StructureElement[] = [];
for (const unitGroup of unitGroups) {
const unit = unitGroup.units[0];
if (unit.kind !== this.kind) {
continue;
}
const elements = unit.elements;
for (let i = 0, _i = elements.length; i < _i; i++) {
const e = elements[i];
if (!this.has(e) || seenIndices.has(e)) continue;
seenIndices.add(elements[i]);
loci[loci.length] = StructureElement.create(unit, e);
}
}
loci.sort((x, y) => x.element - y.element);
return loci;
getExportContext(structure: Structure): IndexedCustomProperty.ExportCtx<T> {
const elements = this.getStructureElements(structure);
return { elements, property: i => this.get(elements[i].element)! };
constructor(private map: Map<ElementIndex, T>) {
this.kind = Unit.Kind.Atomic;
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
class EntityMappedCustomProperty<T = any> implements IndexedCustomProperty<EntityIndex, T> {
readonly id: UUID = UUID.create();
readonly kind: Unit.Kind;
readonly level = 'entity';
has(idx: EntityIndex): boolean { return this.map.has(idx); }
get(idx: EntityIndex) { return this.map.get(idx); }
private getStructureElements(structure: Structure) {
const models = structure.models;
if (models.length !== 1) throw new Error(`Only works on structures with a single model.`);
const index = models[0].atomicHierarchy.index;
const seenIndices = new Set<EntityIndex>();
const unitGroups = structure.unitSymmetryGroups;
const loci: StructureElement[] = [];
const segments = models[0].atomicHierarchy.chainAtomSegments;
for (const unitGroup of unitGroups) {
const unit = unitGroup.units[0];
if (unit.kind !== this.kind) {
continue;
}
const chains = Segmentation.transientSegments(segments, unit.elements);
while (chains.hasNext) {
const seg = chains.move();
const eI = index.getEntityFromChain(seg.index);
if (!this.has(eI) || seenIndices.has(eI)) continue;
seenIndices.add(eI);
loci[loci.length] = StructureElement.create(unit, unit.elements[seg.start]);
}
}
loci.sort((x, y) => x.element - y.element);
return loci;
}
getExportContext(structure: Structure): IndexedCustomProperty.ExportCtx<T> {
const elements = this.getStructureElements(structure);
const chainIndex = structure.model.atomicHierarchy.chainAtomSegments.index;
const index = structure.model.atomicHierarchy.index;
return { elements, property: i => this.get(index.getEntityFromChain(chainIndex[elements[i].element]))! };
}
constructor(private map: Map<EntityIndex, T>) {
this.kind = Unit.Kind.Atomic;