Skip to content
Snippets Groups Projects
Commit d1b49fda authored by Alexander Rose's avatar Alexander Rose
Browse files

wip, carbohydrates visual

parent aa84842f
No related branches found
No related tags found
No related merge requests found
......@@ -28,7 +28,7 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru
const t = Mat4.identity()
const p = Vec3.zero()
const { carbohydrates } = structure
const carbohydrates = structure.carbohydrates
const linkParams = { radiusTop: 0.2, radiusBottom: 0.2 }
......
......@@ -147,6 +147,10 @@ export const WaterNames = [
'SOL', 'WAT', 'HOH', 'H2O', 'W', 'DOD', 'D3O', 'TIP3', 'TIP4', 'SPC'
]
export const ExtraSaccharideNames = [
'MLR'
]
export const RnaBaseNames = [ 'A', 'C', 'T', 'G', 'I', 'U' ]
export const DnaBaseNames = [ 'DA', 'DC', 'DT', 'DG', 'DI', 'DU' ]
export const PurinBaseNames = [ 'A', 'G', 'DA', 'DG', 'DI' ]
......@@ -166,7 +170,7 @@ export function getMoleculeType(compType: string, compId: string) {
return MoleculeType.RNA
} else if (DNAComponentTypeNames.includes(compType)) {
return MoleculeType.DNA
} else if (SaccharideComponentTypeNames.includes(compType)) {
} else if (SaccharideComponentTypeNames.includes(compType) || ExtraSaccharideNames.includes(compId)) {
return MoleculeType.saccharide
} else if (WaterNames.includes(compId)) {
return MoleculeType.water
......
......@@ -9,9 +9,13 @@ import { ResidueIndex } from '../../model';
import { Interval, Segmentation } from 'mol-data/int';
import Structure from '../structure';
import { Carbohydrates, CarbohydrateLink, CarbohydrateTerminalLink, CarbohydrateElement } from './data';
import { SaccharideNameMap } from './constants';
import { SaccharideNameMap, UnknownSaccharideComponent } from './constants';
import { Vec3 } from 'mol-math/linear-algebra';
import { getCenterAndRadius } from '../../util';
import { getCenterAndRadius, getMoleculeType } from '../../util';
import { MoleculeType } from '../../model/types';
import { areConnected } from 'mol-math/graph';
import { combinations } from 'mol-data/util/combination';
import { fillSerial } from 'mol-util/array';
function getResidueIndex(elementIndex: number, unit: Unit.Atomic) {
return unit.model.atomicHierarchy.residueAtomSegments.index[unit.elements[elementIndex]]
......@@ -21,17 +25,19 @@ function getRingIndices(unit: Unit.Atomic, rI: ResidueIndex) {
const { offsets } = unit.model.atomicHierarchy.residueAtomSegments
const { elements } = unit
const interval = Interval.ofBounds(offsets[rI], offsets[rI + 1])
const rings = unit.rings.byFingerprint.get('C-C-C-C-C-O') || unit.rings.byFingerprint.get('C-C-C-C-O')
if (rings) {
for (let i = 0, il = rings.length; i < il; ++i) {
let withinIntervalCount = 0
const ring = unit.rings.all[rings[i]]
for (let j = 0, jl = ring.length; j < jl; ++j) {
if (Interval.has(interval, elements[ring[j]])) ++withinIntervalCount
}
if (withinIntervalCount === ring.length) return ring
const rings: number[] = []
rings.push(...unit.rings.byFingerprint.get('C-C-C-C-C-O') || [])
rings.push(...unit.rings.byFingerprint.get('C-C-C-C-O') || [])
const sugarRings: ReadonlyArray<number>[] = []
for (let i = 0, il = rings.length; i < il; ++i) {
let withinIntervalCount = 0
const ring = unit.rings.all[rings[i]]
for (let j = 0, jl = ring.length; j < jl; ++j) {
if (Interval.has(interval, elements[ring[j]])) ++withinIntervalCount
}
if (withinIntervalCount === ring.length) sugarRings.push(ring)
}
return sugarRings
}
export function computeCarbohydrates(structure: Structure): Carbohydrates {
......@@ -45,6 +51,7 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
return `${residueIndex}|${unitId}`
}
// get carbohydrate elements and carbohydrate links induced by intra-residue bonds
for (let i = 0, il = structure.units.length; i < il; ++i) {
const unit = structure.units[i]
if (!Unit.isAtomic(unit)) continue
......@@ -62,66 +69,87 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
while (residueIt.hasNext) {
const { index: residueIndex } = residueIt.move();
const saccharideComp = SaccharideNameMap.get(label_comp_id.value(residueIndex))
if (!saccharideComp) continue
const saccharideComp = SaccharideNameMap.get(label_comp_id.value(residueIndex)) || UnknownSaccharideComponent
if (saccharideComp === UnknownSaccharideComponent) {
if (getMoleculeType(unit.model, residueIndex) !== MoleculeType.saccharide) continue
}
const sugarRings = getRingIndices(unit, residueIndex)
const ringElements: number[] = []
console.log('sugarRings', sugarRings)
const ringIndices = getRingIndices(unit, residueIndex)
if (ringIndices) {
for (let j = 0, jl = sugarRings.length; j < jl; ++j) {
const center = Vec3.zero()
const normal = Vec3.zero()
const direction = Vec3.zero()
const ringRadius = getCenterAndRadius(center, unit, ringIndices)
console.log(ringRadius, center)
const elementIndex = elements.length
getCenterAndRadius(center, unit, sugarRings[j])
elementsMap.set(elementKey(residueIndex, unit.id), elements.length)
ringElements.push(elementIndex)
elementsMap.set(elementKey(residueIndex, unit.id), elementIndex)
elements.push({ center, normal, direction, unit, residueIndex, component: saccharideComp })
} else {
}
// add carbohydrate links induced by intra-residue bonds
const ringCombinations = combinations(fillSerial(new Array(sugarRings.length)), 2)
for (let j = 0, jl = ringCombinations.length; j < jl; ++j) {
const rc = ringCombinations[j]
if (areConnected(sugarRings[rc[0]], sugarRings[rc[1]], unit.links, 2)) {
links.push({
carbohydrateIndexA: ringElements[rc[0]],
carbohydrateIndexB: ringElements[rc[1]]
})
links.push({
carbohydrateIndexA: ringElements[rc[1]],
carbohydrateIndexB: ringElements[rc[0]]
})
}
}
if (!sugarRings.length) {
console.warn('No ring found for carbohydrate')
}
}
}
}
elementsMap.forEach((elementIndex, key) => {
const unit = elements[elementIndex].unit
// get carbohydrate links induced by inter-unit bonds
for (let i = 0, il = structure.units.length; i < il; ++i) {
const unit = structure.units[i]
if (!Unit.isAtomic(unit)) continue
structure.links.getLinkedUnits(unit).forEach(pairBonds => {
pairBonds.linkedElementIndices.forEach(indexA => {
pairBonds.getBonds(indexA).forEach(bondInfo => {
let { unitA, unitB } = pairBonds
let indexB = bondInfo.indexB
let residueIndexA = getResidueIndex(indexA, unitA)
let residueIndexB = getResidueIndex(indexB, unitB)
let keyA = elementKey(residueIndexA, unitA.id)
let keyB = elementKey(residueIndexB, unitB.id)
if (key === keyB) {
[keyB, keyA] = [keyA, keyB];
[indexB, indexA] = [indexA, indexB];
[unitB, unitA] = [unitA, unitB];
}
const elementIndexB = elementsMap.get(keyB)
if (elementIndexB !== undefined) {
const { unitA, unitB } = pairBonds
const indexB = bondInfo.indexB
const elementIndexA = elementsMap.get(elementKey(getResidueIndex(indexA, unitA), unitA.id))
const elementIndexB = elementsMap.get(elementKey(getResidueIndex(indexB, unitB), unitB.id))
if (elementIndexA !== undefined && elementIndexB !== undefined) {
links.push({
carbohydrateIndexA: elementIndex,
carbohydrateIndexA: elementIndexA,
carbohydrateIndexB: elementIndexB
})
} else {
} else if (elementIndexA !== undefined) {
terminalLinks.push({
carbohydrateIndex: elementIndex,
carbohydrateIndex: elementIndexA,
elementIndex: indexB,
elementUnit: unitB,
fromCarbohydrate: true
})
} else if (elementIndexB !== undefined) {
terminalLinks.push({
carbohydrateIndex: elementIndex,
elementIndex: indexB,
elementUnit: unitB,
carbohydrateIndex: elementIndexB,
elementIndex: indexA,
elementUnit: unitA,
fromCarbohydrate: false
})
}
})
})
})
})
}
return { links, terminalLinks, elements }
}
\ No newline at end of file
......@@ -77,6 +77,13 @@ export type SaccharideComponent = {
type: SaccharideType
}
export const UnknownSaccharideComponent = {
abbr: 'Unk',
name: 'Unknown',
color: SaccharideColors.Secondary,
type: SaccharideType.Unknown
}
const Monosaccharides: SaccharideComponent[] = [
{ abbr: 'Glc', name: 'Glucose', color: SaccharideColors.Blue, type: SaccharideType.Hexose },
{ abbr: 'Man', name: 'Mannose', color: SaccharideColors.Green, type: SaccharideType.Hexose },
......
......@@ -78,7 +78,7 @@ export class Stage {
// this.loadPdbid('1hrv') // viral assembly
// this.loadPdbid('1rb8') // virus
// this.loadPdbid('1blu') // metal coordination
this.loadPdbid('3pqr') // inter unit bonds, two polymer chains, ligands, water, carbohydrates linked to protein
// this.loadPdbid('3pqr') // inter unit bonds, two polymer chains, ligands, water, carbohydrates linked to protein
// this.loadPdbid('4v5a') // ribosome
// this.loadPdbid('3j3q') // ...
// this.loadPdbid('2np2') // dna
......@@ -91,10 +91,15 @@ export class Stage {
// this.loadPdbid('1y26') // rna
// this.loadPdbid('1xv6') // rna, modified nucleotides
// this.loadPdbid('3bbm') // rna with linker
// this.loadPdbid('1euq') // t-rna
// this.loadPdbid('2e2i') // rna, dna, protein
// this.loadPdbid('1gfl') // GFP, flourophore has carbonyl oxygen removed
// this.loadPdbid('1sfi') // contains cyclic peptid
// this.loadPdbid('3sn6') // discontinuous chains
// this.loadPdbid('2zex') // small, contains carbohydrate polymer
// this.loadPdbid('2gdu') // contains sucrose
// this.loadPdbid('2fnc') // contains maltotriose
this.loadPdbid('4zs9') // contains raffinose
// this.loadPdbid('2b5t') // contains large carbohydrate polymer
// this.loadMmcifUrl(`../../examples/1cbs_full.bcif`)
// this.loadMmcifUrl(`../../examples/1cbs_updated.cif`)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment