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';
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,
getElements(structure: Structure): IndexedCustomProperty.Elements<T>
}
export namespace IndexedCustomProperty {
export type Index = ElementIndex | ResidueIndex | ChainIndex | EntityIndex
export type Level = 'atom' | 'residue' | 'chain' | 'entity'
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];
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
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 as number]);
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]);
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;
}
getElements(structure: Structure): IndexedCustomProperty.Elements<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;
getElements(structure: Structure): IndexedCustomProperty.Elements<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
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;
}
getElements(structure: Structure): IndexedCustomProperty.Elements<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;