From 25965d307a838c400936d320e6ce33eac39be4c8 Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Tue, 26 Jun 2018 12:10:15 +0200 Subject: [PATCH] chem_comp_bond as custom property --- src/apps/structure-info/model.ts | 2 +- src/mol-data/int/impl/segmentation.ts | 6 +- src/mol-data/int/segmentation.ts | 4 +- .../export/categories/chem_comp_bond.ts | 7 -- src/mol-model/structure/export/mmcif.ts | 6 ++ .../structure/model/formats/mmcif.ts | 6 ++ .../model/formats/mmcif/bonds/comp.ts | 67 +++++++++++++++++-- .../model/properties/custom/collection.ts | 12 ++-- .../model/properties/custom/descriptor.ts | 11 +-- .../structure/unit/links/intra-compute.ts | 2 +- 10 files changed, 94 insertions(+), 29 deletions(-) delete mode 100644 src/mol-model/structure/export/categories/chem_comp_bond.ts diff --git a/src/apps/structure-info/model.ts b/src/apps/structure-info/model.ts index d1252937c..f34db7c79 100644 --- a/src/apps/structure-info/model.ts +++ b/src/apps/structure-info/model.ts @@ -207,7 +207,7 @@ async function run(frame: CifFrame) { printUnits(structure); printSymmetryInfo(models[0]); //printRings(structure); - //printLinks(structure, true, true); + printLinks(structure, true, true); //printModRes(models[0]); //printSecStructure(models[0]); } diff --git a/src/mol-data/int/impl/segmentation.ts b/src/mol-data/int/impl/segmentation.ts index 63313431f..a5534e52a 100644 --- a/src/mol-data/int/impl/segmentation.ts +++ b/src/mol-data/int/impl/segmentation.ts @@ -54,7 +54,7 @@ export class SegmentIterator<T extends number = number> implements Iterator<Segs private segmentMin = 0; private segmentMax = 0; private setRange = Interval.Empty; - private value: Segs.Segment<T> = { index: 0, start: 0, end: 0 }; + private value: Segs.Segment<T> = { index: 0, start: 0 as T, end: 0 as T }; hasNext: boolean = false; @@ -75,8 +75,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); - this.value.end = setEnd; + this.value.start = Interval.start(this.setRange) as T; + this.value.end = setEnd as T; this.setRange = Interval.ofBounds(setEnd, Interval.end(this.setRange)); return setEnd > this.value.start; } diff --git a/src/mol-data/int/segmentation.ts b/src/mol-data/int/segmentation.ts index 8f7c58db6..94abaaa0f 100644 --- a/src/mol-data/int/segmentation.ts +++ b/src/mol-data/int/segmentation.ts @@ -9,7 +9,7 @@ import OrderedSet from './ordered-set' import * as Impl from './impl/segmentation' namespace Segmentation { - export interface Segment<T extends number = number> { index: number, start: number, end: number } + export interface Segment<T extends number = number> { index: number, start: T, end: T } 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; @@ -19,7 +19,7 @@ namespace Segmentation { export const projectValue: <T extends number = number>(segs: Segmentation<T>, 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 = Impl.segments as any; + export const transientSegments: <T extends number = number>(segs: Segmentation<T>, set: OrderedSet<T>, segment?: Segment<T>) => Impl.SegmentIterator<T> = Impl.segments as any; } interface Segmentation<T extends number = number> { diff --git a/src/mol-model/structure/export/categories/chem_comp_bond.ts b/src/mol-model/structure/export/categories/chem_comp_bond.ts deleted file mode 100644 index 66298c566..000000000 --- a/src/mol-model/structure/export/categories/chem_comp_bond.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -// TODO \ No newline at end of file diff --git a/src/mol-model/structure/export/mmcif.ts b/src/mol-model/structure/export/mmcif.ts index 4458ef154..f09ee2103 100644 --- a/src/mol-model/structure/export/mmcif.ts +++ b/src/mol-model/structure/export/mmcif.ts @@ -63,6 +63,12 @@ export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structure: S for (const cat of Categories) { encoder.writeCategory(cat, ctx); } + for (const customProp of model.customProperties.all) { + const cats = customProp.cifExport.categoryProvider(ctx[0]); + for (const cat of cats) { + encoder.writeCategory(cat, ctx); + } + } } function to_mmCIF(name: string, structure: Structure, asBinary = false) { diff --git a/src/mol-model/structure/model/formats/mmcif.ts b/src/mol-model/structure/model/formats/mmcif.ts index 66c2bfc28..6bd490457 100644 --- a/src/mol-model/structure/model/formats/mmcif.ts +++ b/src/mol-model/structure/model/formats/mmcif.ts @@ -27,6 +27,7 @@ import { Element } from '../../../structure' import { CustomProperties } from '../properties/custom'; import mmCIF_Format = Format.mmCIF +import { ComponentBond } from './mmcif/bonds'; type AtomSite = mmCIF_Database['atom_site'] function findModelEnd({ data }: mmCIF_Format, startIndex: number) { @@ -201,6 +202,10 @@ function createModel(format: mmCIF_Format, atom_site: AtomSite, previous?: Model }; } +function attachProps(model: Model) { + ComponentBond.attachFromMmCif(model); +} + function buildModels(format: mmCIF_Format): Task<ReadonlyArray<Model>> { return Task.create('Create mmCIF Model', async ctx => { const atomCount = format.data.atom_site._rowCount; @@ -218,6 +223,7 @@ function buildModels(format: mmCIF_Format): Task<ReadonlyArray<Model>> { const modelEnd = findModelEnd(format, modelStart); const atom_site = await sortAtomSite(ctx, format.data.atom_site, modelStart, modelEnd); const model = createModel(format, atom_site, models.length > 0 ? models[models.length - 1] : void 0); + attachProps(model); models.push(model); modelStart = modelEnd; } 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 4b31456ab..fe0c4ab4e 100644 --- a/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts +++ b/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts @@ -7,12 +7,50 @@ 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 { Segmentation } from 'mol-data/int'; +import { CifWriter } from 'mol-io/writer/cif' export interface ComponentBond { entries: Map<string, ComponentBond.Entry> } export namespace ComponentBond { + export const Descriptor: ModelPropertyDescriptor = { + isStatic: true, + name: 'chem_comp_bond', + cifExport: { + categoryNames: ['chem_comp_bond'], + categoryProvider(ctx) { + const comp_names = getUniqueResidueNames(ctx.structure); + const chem_comp_bond = getChemCompBond(ctx.model); + + if (!chem_comp_bond) return []; + + 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', chem_comp_bond, indices) + ]; + } + } + } + + export function attachFromMmCif(model: Model): boolean { + if (model.sourceData.kind !== 'mmCIF') return false; + const { chem_comp_bond } = model.sourceData.data; + if (chem_comp_bond._rowCount === 0) return false; + model.customProperties.add(Descriptor); + model._staticPropertyData.__ComponentBondData__ = chem_comp_bond; + return true; + } + export class ComponentBondImpl implements ComponentBond { entries: Map<string, ComponentBond.Entry> = new Map(); @@ -46,13 +84,15 @@ export namespace ComponentBond { } } + function getChemCompBond(model: Model) { + return model._staticPropertyData.__ComponentBondData__ as mmCIF_Database['chem_comp_bond']; + } + export const PropName = '__ComponentBond__'; - export function fromModel(model: Model): ComponentBond | undefined { + export function get(model: Model): ComponentBond | undefined { if (model._staticPropertyData[PropName]) return model._staticPropertyData[PropName]; - - if (model.sourceData.kind !== 'mmCIF') return - const { chem_comp_bond } = model.sourceData.data; - if (!chem_comp_bond._rowCount) return void 0; + if (!model.customProperties.has(Descriptor)) return void 0; + const chem_comp_bond = getChemCompBond(model); let compBond = new ComponentBondImpl(); @@ -90,4 +130,21 @@ export namespace ComponentBond { model._staticPropertyData[PropName] = compBond; return compBond; } + + function getUniqueResidueNames(s: Structure) { + const prop = StructureProperties.residue.label_comp_id; + const names = new Set<string>(); + const loc = Element.Location(); + for (const unit of s.units) { + if (!Unit.isAtomic(unit)) continue; + const residues = Segmentation.transientSegments(unit.model.atomicHierarchy.residueSegments, unit.elements); + loc.unit = unit; + while (residues.hasNext) { + const seg = residues.move(); + loc.element = seg.start; + names.add(prop(loc)); + } + } + return names; + } } \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/custom/collection.ts b/src/mol-model/structure/model/properties/custom/collection.ts index 75dda9b2d..371889b8f 100644 --- a/src/mol-model/structure/model/properties/custom/collection.ts +++ b/src/mol-model/structure/model/properties/custom/collection.ts @@ -4,22 +4,22 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { PropertyDescriptor } from './descriptor' +import { ModelPropertyDescriptor } from './descriptor' export class CustomProperties { - private _list: PropertyDescriptor[] = []; - private _set = new Set<PropertyDescriptor>(); + private _list: ModelPropertyDescriptor[] = []; + private _set = new Set<ModelPropertyDescriptor>(); - get all(): ReadonlyArray<PropertyDescriptor> { + get all(): ReadonlyArray<ModelPropertyDescriptor> { return this._list; } - add(desc: PropertyDescriptor) { + add(desc: ModelPropertyDescriptor) { this._list.push(desc); this._set.add(desc); } - has(desc: PropertyDescriptor): boolean { + has(desc: ModelPropertyDescriptor): boolean { return this._set.has(desc); } } \ 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 37af3562b..89304d197 100644 --- a/src/mol-model/structure/model/properties/custom/descriptor.ts +++ b/src/mol-model/structure/model/properties/custom/descriptor.ts @@ -7,12 +7,15 @@ import { CifWriter } from 'mol-io/writer/cif' import { CifExportContext } from '../../../export/mmcif'; -interface PropertyDescriptor { +interface ModelPropertyDescriptor { readonly isStatic: boolean, readonly name: string, - /** Given a structure, returns a list of category providers used for export. */ - getCifCategories: (ctx: CifExportContext) => CifWriter.Category.Provider[] + cifExport: { + /** used category names that can be used for "filtering" by the writer */ + readonly categoryNames: ReadonlyArray<string>, + categoryProvider: (ctx: CifExportContext) => CifWriter.Category.Provider[] + } } -export { PropertyDescriptor } \ No newline at end of file +export { ModelPropertyDescriptor } \ No newline at end of file diff --git a/src/mol-model/structure/structure/unit/links/intra-compute.ts b/src/mol-model/structure/structure/unit/links/intra-compute.ts index 3f2949ec9..c17d81539 100644 --- a/src/mol-model/structure/structure/unit/links/intra-compute.ts +++ b/src/mol-model/structure/structure/unit/links/intra-compute.ts @@ -36,7 +36,7 @@ function _computeBonds(unit: Unit.Atomic, params: LinkComputationParameters): In const query3d = unit.lookup3d; const structConn = unit.model.sourceData.kind === 'mmCIF' ? StructConn.fromModel(unit.model) : void 0; - const component = unit.model.sourceData.kind === 'mmCIF' ? ComponentBond.fromModel(unit.model) : void 0; + const component = unit.model.sourceData.kind === 'mmCIF' ? ComponentBond.get(unit.model) : void 0; const atomA: number[] = []; const atomB: number[] = []; -- GitLab