Ver Fonte

wip, carbohydrates orientation

Alexander Rose há 6 anos atrás
pai
commit
1931a08baf

+ 58 - 3
src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts

@@ -22,20 +22,75 @@ import { createMeshValues, updateMeshValues, updateRenderableState, createRender
 import { MeshBuilder } from '../../../shape/mesh-builder';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { createUniformColor } from '../../../util/color-data';
+import { getSaccharideShape, SaccharideShapes } from 'mol-model/structure/structure/carbohydrates/constants';
 
 async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Structure, mesh?: Mesh) {
     const builder = MeshBuilder.create(256, 128, mesh)
 
     const t = Mat4.identity()
     const p = Vec3.zero()
+    const pd = Vec3.zero()
+    const p1 = Vec3.zero()
+    const p2 = Vec3.zero()
     const carbohydrates = structure.carbohydrates
 
-    const linkParams = { radiusTop: 0.2, radiusBottom: 0.2 }
+    function centerAlign(center: Vec3, normal: Vec3, direction: Vec3) {
+        Vec3.add(pd, center, direction)
+        Mat4.targetTo(t, center, pd, normal)
+        Mat4.setTranslation(t, center)
+    }
+
+    const side = 1.75 * 2 * 0.806; // 0.806 == Math.cos(Math.PI / 4)
+    const radius = 1.75
+    const coneParams = { radiusTop: radius, radiusBottom: 0.0, topCap: true }
+
+    const linkParams = { radiusTop: 0.4, radiusBottom: 0.4 }
 
     for (let i = 0, il = carbohydrates.elements.length; i < il; ++i) {
         const c = carbohydrates.elements[i]
-        Mat4.setTranslation(t, c.center)
-        builder.addBox(t, { width: 2, height: 2, depth: 2 })
+        const shapeType = getSaccharideShape(c.component.type)
+        switch (shapeType) {
+            case SaccharideShapes.FilledSphere:
+                builder.addIcosahedron(c.center, radius, 1)
+                break;
+            case SaccharideShapes.FilledCube:
+                centerAlign(c.center, c.normal, c.direction)
+                builder.addBox(t, { width: side, height: side, depth: side })
+                break;
+            case SaccharideShapes.CrossedCube:
+                // TODO split
+                centerAlign(c.center, c.normal, c.direction)
+                builder.addBox(t, { width: side, height: side, depth: side })
+                break;
+            case SaccharideShapes.FilledCone:
+                Vec3.scaleAndAdd(p1, c.center, c.normal, radius)
+                Vec3.scaleAndSub(p2, c.center, c.normal, radius)
+                builder.addCylinder(p1, p2, 1, coneParams)
+                break
+            case SaccharideShapes.DevidedCone:
+                // TODO split
+                Vec3.scaleAndAdd(p1, c.center, c.normal, radius)
+                Vec3.scaleAndSub(p2, c.center, c.normal, radius)
+                builder.addCylinder(p1, p2, 1, coneParams)
+                break
+            case SaccharideShapes.FlatBox:
+                centerAlign(c.center, c.normal, c.direction)
+                builder.addBox(t, { width: side, height: side / 2, depth: side })
+                break
+            case SaccharideShapes.FilledDiamond:
+            case SaccharideShapes.DividedDiamond:
+            case SaccharideShapes.FilledStar:
+            case SaccharideShapes.FlatDiamond:
+            case SaccharideShapes.Pentagon:
+                centerAlign(c.center, c.normal, c.direction)
+                builder.addBox(t, { width: side, height: 0.5, depth: side })
+                break
+            case SaccharideShapes.FlatHexagon:
+            default:
+                centerAlign(c.center, c.normal, c.direction)
+                builder.addBox(t, { width: side, height: 0.1, depth: side })
+                break
+        }
     }
 
     for (let i = 0, il = carbohydrates.links.length; i < il; ++i) {

+ 8 - 0
src/mol-math/linear-algebra/3d/vec3.ts

@@ -132,6 +132,14 @@ namespace Vec3 {
         return out;
     }
 
+    /** Scales b, then subtracts b from a */
+    export function scaleAndSub(out: Vec3, a: Vec3, b: Vec3, scale: number) {
+        out[0] = a[0] - (b[0] * scale);
+        out[1] = a[1] - (b[1] * scale);
+        out[2] = a[2] - (b[2] * scale);
+        return out;
+    }
+
     /**
      * Math.round the components of a Vec3
      */

+ 35 - 8
src/mol-model/structure/structure/carbohydrates/compute.ts

@@ -11,11 +11,12 @@ import Structure from '../structure';
 import { Carbohydrates, CarbohydrateLink, CarbohydrateTerminalLink, CarbohydrateElement } from './data';
 import { SaccharideNameMap, UnknownSaccharideComponent } from './constants';
 import { Vec3 } from 'mol-math/linear-algebra';
-import { getCenterAndRadius, getMoleculeType } from '../../util';
-import { MoleculeType } from '../../model/types';
+import { getMoleculeType, getPositionMatrix } from '../../util';
+import { MoleculeType, ElementSymbol } from '../../model/types';
 import { areConnected } from 'mol-math/graph';
 import { combinations } from 'mol-data/util/combination';
 import { fillSerial } from 'mol-util/array';
+import PrincipalAxes from 'mol-math/linear-algebra/matrix/principal-axes';
 
 function getResidueIndex(elementIndex: number, unit: Unit.Atomic) {
     return unit.model.atomicHierarchy.residueAtomSegments.index[unit.elements[elementIndex]]
@@ -40,6 +41,32 @@ function getRingIndices(unit: Unit.Atomic, rI: ResidueIndex) {
     return sugarRings
 }
 
+const C = ElementSymbol('C')
+function getDirection(direction: Vec3, unit: Unit.Atomic, indices: ReadonlyArray<number>, center: Vec3) {
+    let indexC1 = -1, indexC1X = -1, indexC = -1
+    const { elements } = unit
+    const { position } = unit.conformation
+    const { label_atom_id, type_symbol } = unit.model.atomicHierarchy.atoms
+    for (let i = 0, il = indices.length; i < il; ++i) {
+        const ei = elements[indices[i]]
+        const atomId = label_atom_id.value(ei)
+        if (atomId === 'C1') {
+            indexC1 = ei
+            break
+        } else if (indexC1X === -1 && atomId.startsWith('C1')) {
+            indexC1X = ei
+        } else if (indexC === -1 && type_symbol.value(ei) === C) {
+            indexC = ei
+        }
+    }
+    const index = indexC1 !== -1 ? indexC1
+        : indexC1X !== -1 ? indexC1X
+        : indexC !== -1 ? indexC
+        : elements[indices[0]]
+    Vec3.normalize(direction, Vec3.sub(direction, center, position(index, direction)))
+    return direction
+}
+
 export function computeCarbohydrates(structure: Structure): Carbohydrates {
     const links: CarbohydrateLink[] = []
     const terminalLinks: CarbohydrateTerminalLink[] = []
@@ -76,15 +103,15 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
 
                 const sugarRings = getRingIndices(unit, residueIndex)
                 const ringElements: number[] = []
-                console.log('sugarRings', sugarRings)
 
                 for (let j = 0, jl = sugarRings.length; j < jl; ++j) {
-                    const center = Vec3.zero()
-                    const normal = Vec3.zero()
-                    const direction = Vec3.zero()
-                    const elementIndex = elements.length
-                    getCenterAndRadius(center, unit, sugarRings[j])
+                    const pa = new PrincipalAxes(getPositionMatrix(unit, sugarRings[j]))
+                    const center = Vec3.copy(Vec3.zero(), pa.center)
+                    const normal = Vec3.copy(Vec3.zero(), pa.normVecC)
+                    const direction = getDirection(Vec3.zero(), unit, sugarRings[j], center)
+                    Vec3.orthogonalize(direction, normal, direction)
 
+                    const elementIndex = elements.length
                     ringElements.push(elementIndex)
                     elementsMap.set(elementKey(residueIndex, unit.id), elementIndex)
                     elements.push({ center, normal, direction, unit, residueIndex, component: saccharideComp })

+ 13 - 3
src/mol-model/structure/structure/carbohydrates/constants.ts

@@ -175,7 +175,11 @@ const Monosaccharides: SaccharideComponent[] = [
 
 const CommonSaccharideNames: { [k: string]: string[] } = {
     // Hexose
-    Glc: ['GLC', 'BGC'],
+    Glc: [
+        'GLC', 'BGC',
+        'BOG', // via GlyFinder
+        'TRE', // via GlyFinder, disaccharide but homomer
+    ],
     Man: ['MAN', 'BMA'],
     Gal: ['GAL', 'GLA'],
     Gul: ['GUP', 'GL0'],
@@ -192,7 +196,10 @@ const CommonSaccharideNames: { [k: string]: string[] } = {
     TalNAc: [],
     IdoNAc: ['HSQ'],
     // Hexosamine
-    GlcN: ['GCS', 'PA1'],
+    GlcN: [
+        'GCS', 'PA1',
+        'IDU', 'SGN', 'SUS', // via GlyFinder
+    ],
     ManN: ['95Z'],
     GalN: ['X6X', '1GN'],
     GulN: [],
@@ -208,7 +215,10 @@ const CommonSaccharideNames: { [k: string]: string[] } = {
     AltA: [],
     AllA: [],
     TalA: ['X0X', 'X1X'],
-    IdoA: ['IDR'],
+    IdoA: [
+        'IDR',
+        'IDS', // via GlyFinder
+    ],
     // Deoxyhexose
     Qui: ['G6D'],
     Rha: ['RAM', 'RM4'],

+ 13 - 0
src/mol-model/structure/util.ts

@@ -8,6 +8,7 @@ import { Model, ResidueIndex, ElementIndex } from './model';
 import { MoleculeType, AtomRole, MoleculeTypeAtomRoleId } from './model/types';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Unit } from './structure';
+import Matrix from 'mol-math/linear-algebra/matrix/matrix';
 
 export function getMoleculeType(model: Model, rI: ResidueIndex) {
     const compId = model.atomicHierarchy.residues.label_comp_id.value(rI)
@@ -60,4 +61,16 @@ export function getCenterAndRadius(centroid: Vec3, unit: Unit, indices: ArrayLik
     }
     Vec3.scale(centroid, centroid, 1/indices.length)
     return Vec3.distance(centerMin, centroid)
+}
+
+const matrixPos = Vec3.zero()
+export function getPositionMatrix(unit: Unit, indices: ArrayLike<number>) {
+    const pos = unit.conformation.position
+    const mat = Matrix.create(3, indices.length)
+    const { elements } = unit
+    for (let i = 0, il = indices.length; i < il; ++i) {
+        pos(elements[indices[i]], matrixPos)
+        Vec3.toArray(matrixPos, mat.data, i * 3)
+    }
+    return mat
 }

+ 8 - 2
src/mol-view/stage.ts

@@ -96,14 +96,20 @@ export class Stage {
         // 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('2zex') // contains carbohydrate polymer
+        // this.loadPdbid('3sgj') // contains carbohydrate polymer
+        // this.loadPdbid('3ina') // contains GlcN and IdoA
+        this.loadPdbid('1umz') // contains Xyl (Xyloglucan)
+        // this.loadPdbid('1mfb') // contains Abe
         // this.loadPdbid('2gdu') // contains sucrose
         // this.loadPdbid('2fnc') // contains maltotriose
-        this.loadPdbid('4zs9') // contains raffinose
+        // this.loadPdbid('4zs9') // contains raffinose
+        // this.loadPdbid('2yft') // contains kestose
         // this.loadPdbid('2b5t') // contains large carbohydrate polymer
         // this.loadMmcifUrl(`../../examples/1cbs_full.bcif`)
         // this.loadMmcifUrl(`../../examples/1cbs_updated.cif`)
         // this.loadMmcifUrl(`../../examples/1crn.cif`)
+        // this.loadPdbid('1zag') // temp
 
         // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000001.cif`) // ok
         // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000002.cif`) // ok