Skip to content
Snippets Groups Projects
Commit 25965d30 authored by David Sehnal's avatar David Sehnal
Browse files

chem_comp_bond as custom property

parent c1d54fac
No related branches found
No related tags found
No related merge requests found
...@@ -207,7 +207,7 @@ async function run(frame: CifFrame) { ...@@ -207,7 +207,7 @@ async function run(frame: CifFrame) {
printUnits(structure); printUnits(structure);
printSymmetryInfo(models[0]); printSymmetryInfo(models[0]);
//printRings(structure); //printRings(structure);
//printLinks(structure, true, true); printLinks(structure, true, true);
//printModRes(models[0]); //printModRes(models[0]);
//printSecStructure(models[0]); //printSecStructure(models[0]);
} }
......
...@@ -54,7 +54,7 @@ export class SegmentIterator<T extends number = number> implements Iterator<Segs ...@@ -54,7 +54,7 @@ export class SegmentIterator<T extends number = number> implements Iterator<Segs
private segmentMin = 0; private segmentMin = 0;
private segmentMax = 0; private segmentMax = 0;
private setRange = Interval.Empty; 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; hasNext: boolean = false;
...@@ -75,8 +75,8 @@ export class SegmentIterator<T extends number = number> implements Iterator<Segs ...@@ -75,8 +75,8 @@ export class SegmentIterator<T extends number = number> implements Iterator<Segs
const segmentEnd = this.segments[this.segmentMin + 1]; const segmentEnd = this.segments[this.segmentMin + 1];
// TODO: add optimized version for interval and array? // TODO: add optimized version for interval and array?
const setEnd = OrderedSet.findPredecessorIndexInInterval(this.set, segmentEnd, this.setRange); const setEnd = OrderedSet.findPredecessorIndexInInterval(this.set, segmentEnd, this.setRange);
this.value.start = Interval.start(this.setRange); this.value.start = Interval.start(this.setRange) as T;
this.value.end = setEnd; this.value.end = setEnd as T;
this.setRange = Interval.ofBounds(setEnd, Interval.end(this.setRange)); this.setRange = Interval.ofBounds(setEnd, Interval.end(this.setRange));
return setEnd > this.value.start; return setEnd > this.value.start;
} }
......
...@@ -9,7 +9,7 @@ import OrderedSet from './ordered-set' ...@@ -9,7 +9,7 @@ import OrderedSet from './ordered-set'
import * as Impl from './impl/segmentation' import * as Impl from './impl/segmentation'
namespace 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 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 ofOffsets: <T extends number = number>(offsets: ArrayLike<T>, bounds: Interval) => Segmentation<T> = Impl.ofOffsets as any;
...@@ -19,7 +19,7 @@ namespace Segmentation { ...@@ -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; 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. // 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> { interface Segmentation<T extends number = number> {
......
/**
* 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
...@@ -63,6 +63,12 @@ export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structure: S ...@@ -63,6 +63,12 @@ export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structure: S
for (const cat of Categories) { for (const cat of Categories) {
encoder.writeCategory(cat, ctx); 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) { function to_mmCIF(name: string, structure: Structure, asBinary = false) {
......
...@@ -27,6 +27,7 @@ import { Element } from '../../../structure' ...@@ -27,6 +27,7 @@ import { Element } from '../../../structure'
import { CustomProperties } from '../properties/custom'; import { CustomProperties } from '../properties/custom';
import mmCIF_Format = Format.mmCIF import mmCIF_Format = Format.mmCIF
import { ComponentBond } from './mmcif/bonds';
type AtomSite = mmCIF_Database['atom_site'] type AtomSite = mmCIF_Database['atom_site']
function findModelEnd({ data }: mmCIF_Format, startIndex: number) { function findModelEnd({ data }: mmCIF_Format, startIndex: number) {
...@@ -201,6 +202,10 @@ function createModel(format: mmCIF_Format, atom_site: AtomSite, previous?: Model ...@@ -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>> { function buildModels(format: mmCIF_Format): Task<ReadonlyArray<Model>> {
return Task.create('Create mmCIF Model', async ctx => { return Task.create('Create mmCIF Model', async ctx => {
const atomCount = format.data.atom_site._rowCount; const atomCount = format.data.atom_site._rowCount;
...@@ -218,6 +223,7 @@ function buildModels(format: mmCIF_Format): Task<ReadonlyArray<Model>> { ...@@ -218,6 +223,7 @@ function buildModels(format: mmCIF_Format): Task<ReadonlyArray<Model>> {
const modelEnd = findModelEnd(format, modelStart); const modelEnd = findModelEnd(format, modelStart);
const atom_site = await sortAtomSite(ctx, format.data.atom_site, modelStart, modelEnd); 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); const model = createModel(format, atom_site, models.length > 0 ? models[models.length - 1] : void 0);
attachProps(model);
models.push(model); models.push(model);
modelStart = modelEnd; modelStart = modelEnd;
} }
......
...@@ -7,12 +7,50 @@ ...@@ -7,12 +7,50 @@
import Model from '../../../model' import Model from '../../../model'
import { LinkType } from '../../../types' 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 { export interface ComponentBond {
entries: Map<string, ComponentBond.Entry> entries: Map<string, ComponentBond.Entry>
} }
export namespace ComponentBond { 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 { export class ComponentBondImpl implements ComponentBond {
entries: Map<string, ComponentBond.Entry> = new Map(); entries: Map<string, ComponentBond.Entry> = new Map();
...@@ -46,13 +84,15 @@ export namespace ComponentBond { ...@@ -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 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._staticPropertyData[PropName]) return model._staticPropertyData[PropName];
if (!model.customProperties.has(Descriptor)) return void 0;
if (model.sourceData.kind !== 'mmCIF') return const chem_comp_bond = getChemCompBond(model);
const { chem_comp_bond } = model.sourceData.data;
if (!chem_comp_bond._rowCount) return void 0;
let compBond = new ComponentBondImpl(); let compBond = new ComponentBondImpl();
...@@ -90,4 +130,21 @@ export namespace ComponentBond { ...@@ -90,4 +130,21 @@ export namespace ComponentBond {
model._staticPropertyData[PropName] = compBond; model._staticPropertyData[PropName] = compBond;
return 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
...@@ -4,22 +4,22 @@ ...@@ -4,22 +4,22 @@
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
*/ */
import { PropertyDescriptor } from './descriptor' import { ModelPropertyDescriptor } from './descriptor'
export class CustomProperties { export class CustomProperties {
private _list: PropertyDescriptor[] = []; private _list: ModelPropertyDescriptor[] = [];
private _set = new Set<PropertyDescriptor>(); private _set = new Set<ModelPropertyDescriptor>();
get all(): ReadonlyArray<PropertyDescriptor> { get all(): ReadonlyArray<ModelPropertyDescriptor> {
return this._list; return this._list;
} }
add(desc: PropertyDescriptor) { add(desc: ModelPropertyDescriptor) {
this._list.push(desc); this._list.push(desc);
this._set.add(desc); this._set.add(desc);
} }
has(desc: PropertyDescriptor): boolean { has(desc: ModelPropertyDescriptor): boolean {
return this._set.has(desc); return this._set.has(desc);
} }
} }
\ No newline at end of file
...@@ -7,12 +7,15 @@ ...@@ -7,12 +7,15 @@
import { CifWriter } from 'mol-io/writer/cif' import { CifWriter } from 'mol-io/writer/cif'
import { CifExportContext } from '../../../export/mmcif'; import { CifExportContext } from '../../../export/mmcif';
interface PropertyDescriptor { interface ModelPropertyDescriptor {
readonly isStatic: boolean, readonly isStatic: boolean,
readonly name: string, readonly name: string,
/** Given a structure, returns a list of category providers used for export. */ cifExport: {
getCifCategories: (ctx: CifExportContext) => CifWriter.Category.Provider[] /** used category names that can be used for "filtering" by the writer */
readonly categoryNames: ReadonlyArray<string>,
categoryProvider: (ctx: CifExportContext) => CifWriter.Category.Provider[]
}
} }
export { PropertyDescriptor } export { ModelPropertyDescriptor }
\ No newline at end of file \ No newline at end of file
...@@ -36,7 +36,7 @@ function _computeBonds(unit: Unit.Atomic, params: LinkComputationParameters): In ...@@ -36,7 +36,7 @@ function _computeBonds(unit: Unit.Atomic, params: LinkComputationParameters): In
const query3d = unit.lookup3d; const query3d = unit.lookup3d;
const structConn = unit.model.sourceData.kind === 'mmCIF' ? StructConn.fromModel(unit.model) : void 0; 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 atomA: number[] = [];
const atomB: number[] = []; const atomB: number[] = [];
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment