Alexander Rose 6 years ago
parent
commit
55dc83fd42

+ 97 - 0
src/apps/canvas/assembly-symmetry.ts

@@ -0,0 +1,97 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { AssemblySymmetry } from "mol-model-props/rcsb/symmetry";
+import { Table } from "mol-data/db";
+import { Color } from "mol-util/color";
+import { MeshBuilder } from "mol-geo/mesh/mesh-builder";
+import { Tensor } from "mol-math/linear-algebra";
+import { addSphere } from "mol-geo/mesh/builder/sphere";
+import { addCylinder } from "mol-geo/mesh/builder/cylinder";
+import { Shape } from "mol-model/shape";
+import { DefaultView } from "./view";
+import { Model } from "mol-model/structure";
+import { ShapeRepresentation, ShapeProps } from "mol-geo/representation/shape";
+
+export function getAxesShape(featureId: number, assemblySymmetry: AssemblySymmetry) {
+    const f = assemblySymmetry.db.rcsb_assembly_symmetry_feature
+    const feature = Table.pickRow(f, i => f.id.value(i) === featureId)
+    if (!feature) return
+
+    const axes = assemblySymmetry.getAxes(featureId)
+    if (!axes._rowCount) return
+
+    const vectorSpace = AssemblySymmetry.Schema.rcsb_assembly_symmetry_axis.start.space;
+
+    const colors: Color[] = []
+    const labels: string[] = []
+
+    const radius = 0.4
+    const cylinderProps = { radiusTop: radius, radiusBottom: radius }
+    const meshBuilder = MeshBuilder.create(256, 128)
+
+    for (let i = 0, il = axes._rowCount; i < il; ++i) {
+        const start = Tensor.toVec3(vectorSpace, axes.start.value(i))
+        const end = Tensor.toVec3(vectorSpace, axes.end.value(i))
+        meshBuilder.setGroup(i)
+        addSphere(meshBuilder, start, radius, 2)
+        addSphere(meshBuilder, end, radius, 2)
+        addCylinder(meshBuilder, start, end, 1, cylinderProps)
+        colors.push(Color(0xCCEE11))
+        labels.push(`Axis ${i + 1} for ${feature.symmetry_value} ${feature.type.toLowerCase()} symmetry`)
+    }
+    const mesh = meshBuilder.getMesh()
+    const shape = Shape.create('Axes', mesh, colors, labels)
+    return shape
+}
+
+export function getClusterColorTheme(featureId: number, assemblySymmetry: AssemblySymmetry) {
+    const f = assemblySymmetry.db.rcsb_assembly_symmetry_feature
+    const feature = Table.pickRow(f, i => f.id.value(i) === featureId)
+    if (!feature) return
+
+    const clusters = assemblySymmetry.getClusters(featureId)
+    if (!clusters._rowCount) return
+
+    for (let i = 0, il = clusters._rowCount; i < il; ++i) {
+        console.log(clusters.members.value(i), clusters.avg_rmsd.value(i), feature.stoichiometry_value, feature.stoichiometry_description)
+    }
+}
+
+export interface SymmetryView extends DefaultView {
+    readonly axes: ShapeRepresentation<ShapeProps> // TODO
+}
+
+export async function SymmetryView(model: Model, assembly: string): Promise<SymmetryView> {
+    const view = await DefaultView(model, assembly)
+    const axesRepr = ShapeRepresentation()
+
+    await AssemblySymmetry.attachFromCifOrAPI(model)
+    const assemblySymmetry = AssemblySymmetry.get(model)
+    console.log(assemblySymmetry)
+    if (assemblySymmetry) {
+        const features = assemblySymmetry.getFeatures(assembly)
+        if (features._rowCount) {
+            const axesShape = getAxesShape(features.id.value(1), assemblySymmetry)
+            console.log(axesShape)
+            if (axesShape) {
+                
+                await axesRepr.create(axesShape, {
+                    colorTheme: { name: 'shape-group' },
+                    // colorTheme: { name: 'uniform', value: Color(0xFFCC22) },
+                    useFog: false // TODO fog not working properly
+                }).run()
+            }
+
+            getClusterColorTheme(features.id.value(0), assemblySymmetry)
+            getClusterColorTheme(features.id.value(1), assemblySymmetry)
+        }
+    }
+
+    return Object.assign({}, view, {
+        axes: axesRepr
+    })
+}

+ 9 - 185
src/apps/canvas/index.ts

@@ -7,26 +7,11 @@
 import './index.html'
 
 import Viewer from 'mol-view/viewer';
-import CIF, { CifBlock } from 'mol-io/reader/cif'
-// import { parse as parseObj } from 'mol-io/reader/obj/parser'
-import { readUrlAs } from 'mol-util/read'
-import { Model, Format, Structure, StructureSymmetry } from 'mol-model/structure';
-import { CartoonRepresentation } from 'mol-geo/representation/structure/representation/cartoon';
-import { BallAndStickRepresentation } from 'mol-geo/representation/structure/representation/ball-and-stick';
 import { EveryLoci } from 'mol-model/loci';
 import { MarkerAction } from 'mol-geo/util/marker-data';
 import { labelFirst } from 'mol-view/label';
-// import { Queries as Q, StructureProperties as SP, StructureSelection, StructureQuery } from 'mol-model/structure';
-import { MeshBuilder } from 'mol-geo/mesh/mesh-builder';
-import { ShapeRepresentation } from 'mol-geo/representation/shape';
-import { /*Vec3, Mat4,*/ Tensor } from 'mol-math/linear-algebra';
-import { Shape } from 'mol-model/shape';
-import { Color } from 'mol-util/color';
-import { addSphere } from 'mol-geo/mesh/builder/sphere';
-// import { Box } from 'mol-geo/primitive/box';
-import { AssemblySymmetry } from 'mol-model-props/rcsb/symmetry';
-import { addCylinder } from 'mol-geo/mesh/builder/cylinder';
-import { Table } from 'mol-data/db';
+import { getCifFromUrl, getModelFromMmcif } from './util';
+import { SymmetryView } from './assembly-symmetry';
 
 const container = document.getElementById('container')
 if (!container) throw new Error('Can not find element with id "container".')
@@ -34,7 +19,7 @@ if (!container) throw new Error('Can not find element with id "container".')
 const canvas = document.getElementById('canvas') as HTMLCanvasElement
 if (!canvas) throw new Error('Can not find element with id "canvas".')
 
-const info = document.getElementById('info') as HTMLCanvasElement
+const info = document.getElementById('info')
 if (!info) throw new Error('Can not find element with id "info".')
 
 const viewer = Viewer.create(canvas, container)
@@ -56,178 +41,17 @@ viewer.input.move.subscribe(({x, y, inside, buttons}) => {
     info.innerText = `${label}`
 })
 
-
-// async function getObjFromUrl(url: string) {
-//     const data = await readUrlAs(url, false) as string
-//     const comp = parseObj(data)
-//     const parsed = await comp.run()
-//     if (parsed.isError) throw parsed
-//     return parsed.result
-// }
-
-async function getCifFromUrl(url: string) {
-    const data = await readUrlAs(url, false)
-    const comp = CIF.parse(data)
-    const parsed = await comp.run()
-    if (parsed.isError) throw parsed
-    return parsed.result.blocks[0]
-}
-
-async function getModelFromMmcif(cif: CifBlock) {
-    const models = await Model.create(Format.mmCIF(cif)).run()
-    return models[0]
-}
-
-async function getStructureFromModel(model: Model, assembly = '1') {
-    const assemblies = model.symmetry.assemblies
-    if (assemblies.length) {
-        return await StructureSymmetry.buildAssembly(Structure.ofModel(model), assembly).run()
-    } else {
-        return Structure.ofModel(model)
-    }
-}
-
-function getAxesShape(featureId: number, assemblySymmetry: AssemblySymmetry) {
-    const f = assemblySymmetry.db.rcsb_assembly_symmetry_feature
-    const feature = Table.pickRow(f, i => f.id.value(i) === featureId)
-    if (!feature) return
-
-    const axes = assemblySymmetry.getAxes(featureId)
-    if (!axes._rowCount) return
-
-    const vectorSpace = AssemblySymmetry.Schema.rcsb_assembly_symmetry_axis.start.space;
-
-    const colors: Color[] = []
-    const labels: string[] = []
-
-    const radius = 0.4
-    const cylinderProps = { radiusTop: radius, radiusBottom: radius }
-    const meshBuilder = MeshBuilder.create(256, 128)
-
-    for (let i = 0, il = axes._rowCount; i < il; ++i) {
-        const start = Tensor.toVec3(vectorSpace, axes.start.value(i))
-        const end = Tensor.toVec3(vectorSpace, axes.end.value(i))
-        meshBuilder.setGroup(i)
-        addSphere(meshBuilder, start, radius, 2)
-        addSphere(meshBuilder, end, radius, 2)
-        addCylinder(meshBuilder, start, end, 1, cylinderProps)
-        colors.push(Color(0xCCEE11))
-        labels.push(`Axis ${i + 1} for ${feature.symmetry_value} ${feature.type.toLowerCase()} symmetry`)
-    }
-    const mesh = meshBuilder.getMesh()
-    const shape = Shape.create('Axes', mesh, colors, labels)
-    return shape
-}
-
-function getClusterColorTheme(featureId: number, assemblySymmetry: AssemblySymmetry) {
-    const f = assemblySymmetry.db.rcsb_assembly_symmetry_feature
-    const feature = Table.pickRow(f, i => f.id.value(i) === featureId)
-    if (!feature) return
-
-    const clusters = assemblySymmetry.getClusters(featureId)
-    if (!clusters._rowCount) return
-
-    for (let i = 0, il = clusters._rowCount; i < il; ++i) {
-        console.log(clusters.members.value(i), clusters.avg_rmsd.value(i), feature.stoichiometry_value, feature.stoichiometry_description)
-    }
-}
-
 async function init() {
     const assembly = '1'
 
     const cif = await getCifFromUrl('https://files.rcsb.org/download/4hhb.cif')
     const model = await getModelFromMmcif(cif)
-    const structure = await getStructureFromModel(model, assembly)
-    viewer.center(structure.boundary.sphere.center)
-
-    // cartoon for whole structure
-    const cartoonRepr = CartoonRepresentation()
-    await cartoonRepr.create(structure, {
-        colorTheme: { name: 'chain-id' },
-        sizeTheme: { name: 'uniform', value: 0.2 },
-        useFog: false // TODO fog not working properly
-    }).run()
-    viewer.add(cartoonRepr)
-
-    // ball+stick for whole structure
-    const ballStickRepr = BallAndStickRepresentation()
-    await ballStickRepr.create(structure, {
-        colorTheme: { name: 'element-symbol' },
-        sizeTheme: { name: 'uniform', value: 0.1 },
-        useFog: false // TODO fog not working properly
-    }).run()
-    viewer.add(ballStickRepr)
-
-    // // create new structure via query
-    // const q1 = Q.generators.atoms({
-    //     residueTest: qtx => SP.residue.label_seq_id(qtx.element) < 7
-    // });
-    // const newStructure = StructureSelection.unionStructure(await StructureQuery.run(q1, structure));
-
-    // // ball+stick for new structure
-    // const newBallStickRepr = BallAndStickRepresentation()
-    // await newBallStickRepr.create(newStructure, {
-    //     colorTheme: { name: 'element-symbol' },
-    //     sizeTheme: { name: 'uniform', value: 0.1 },
-    //     useFog: false // TODO fog not working properly
-    // }).run()
-    // viewer.add(newBallStickRepr)
-
-    // // create a mesh
-    // const meshBuilder = MeshBuilder.create(256, 128)
-    // const colors: Color[] = []
-    // const labels: string[] = []
-    // // red sphere
-    // meshBuilder.setGroup(0)
-    // colors[0] = Color(0xFF2233)
-    // labels[0] = 'red sphere'
-    // addSphere(meshBuilder, Vec3.create(0, 0, 0), 4, 2)
-    // // green cube
-    // meshBuilder.setGroup(1)
-    // colors[1] = Color(0x2233FF)
-    // labels[1] = 'blue cube'
-    // const t = Mat4.identity()
-    // Mat4.fromTranslation(t, Vec3.create(10, 0, 0))
-    // Mat4.scale(t, t, Vec3.create(3, 3, 3))
-    // meshBuilder.add(t, Box())
-    // const mesh = meshBuilder.getMesh()
-    // const mesh = getObjFromUrl('mesh.obj')
-
-    // // create shape from mesh
-    // const shape = Shape.create('myShape', mesh, colors, labels)
-
-    // // add representation from shape
-    // const customRepr = ShapeRepresentation()
-    // await customRepr.create(shape, {
-    //     colorTheme: { name: 'shape-group' },
-    //     // colorTheme: { name: 'uniform', value: Color(0xFFCC22) },
-    //     useFog: false // TODO fog not working properly
-    // }).run()
-    // viewer.add(customRepr)
-
-
-    await AssemblySymmetry.attachFromCifOrAPI(model)
-    const assemblySymmetry = AssemblySymmetry.get(model)
-    console.log(assemblySymmetry)
-    if (assemblySymmetry) {
-        const features = assemblySymmetry.getFeatures(assembly)
-        if (features._rowCount) {
-            const axesShape = getAxesShape(features.id.value(1), assemblySymmetry)
-            console.log(axesShape)
-            if (axesShape) {
-                const customRepr = ShapeRepresentation()
-                await customRepr.create(axesShape, {
-                    colorTheme: { name: 'shape-group' },
-                    // colorTheme: { name: 'uniform', value: Color(0xFFCC22) },
-                    useFog: false // TODO fog not working properly
-                }).run()
-                viewer.add(customRepr)
-            }
-
-            getClusterColorTheme(features.id.value(0), assemblySymmetry)
-            getClusterColorTheme(features.id.value(1), assemblySymmetry)
-        }
-    }
+    
+    const view = await SymmetryView(model, assembly)
+    viewer.center(view.structure.boundary.sphere.center)
+    viewer.add(view.cartoon)
+    viewer.add(view.ballAndStick)
+    viewer.add(view.axes)
 
     // ensure the added representations get rendered, i.e. without mouse input
     viewer.requestDraw()

+ 40 - 0
src/apps/canvas/util.ts

@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import CIF, { CifBlock } from 'mol-io/reader/cif'
+import { readUrlAs } from "mol-util/read";
+import { Model, Format, StructureSymmetry, Structure } from 'mol-model/structure';
+// import { parse as parseObj } from 'mol-io/reader/obj/parser'
+
+// export async function getObjFromUrl(url: string) {
+//     const data = await readUrlAs(url, false) as string
+//     const comp = parseObj(data)
+//     const parsed = await comp.run()
+//     if (parsed.isError) throw parsed
+//     return parsed.result
+// }
+
+export async function getCifFromUrl(url: string) {
+    const data = await readUrlAs(url, false)
+    const comp = CIF.parse(data)
+    const parsed = await comp.run()
+    if (parsed.isError) throw parsed
+    return parsed.result.blocks[0]
+}
+
+export async function getModelFromMmcif(cif: CifBlock) {
+    const models = await Model.create(Format.mmCIF(cif)).run()
+    return models[0]
+}
+
+export async function getStructureFromModel(model: Model, assembly = '0') {
+    const assemblies = model.symmetry.assemblies
+    if (assemblies.length) {
+        return await StructureSymmetry.buildAssembly(Structure.ofModel(model), assembly).run()
+    } else {
+        return Structure.ofModel(model)
+    }
+}

+ 103 - 0
src/apps/canvas/view.ts

@@ -0,0 +1,103 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Model, Structure } from "mol-model/structure";
+import { CartoonRepresentation } from 'mol-geo/representation/structure/representation/cartoon';
+import { BallAndStickRepresentation } from 'mol-geo/representation/structure/representation/ball-and-stick';
+import { getStructureFromModel } from "./util";
+
+export interface DefaultView {
+    readonly model: Model
+    readonly structure: Structure
+    readonly cartoon: CartoonRepresentation
+    readonly ballAndStick: BallAndStickRepresentation
+    
+    setAssembly(assembly: string): void
+}
+
+export async function DefaultView(model: Model, assembly: string): Promise<DefaultView> {
+    const cartoon = CartoonRepresentation()
+    const ballAndStick = BallAndStickRepresentation()
+    
+    let structure: Structure
+
+    async function setAssembly(assembly: string) {
+        structure = await getStructureFromModel(model, assembly)
+        await createRepr()
+    }
+
+    async function createRepr() {
+        await cartoon.create(structure, {
+            colorTheme: { name: 'chain-id' },
+            sizeTheme: { name: 'uniform', value: 0.2 },
+            useFog: false // TODO fog not working properly
+        }).run()
+
+        await ballAndStick.create(structure, {
+            colorTheme: { name: 'element-symbol' },
+            sizeTheme: { name: 'uniform', value: 0.1 },
+            useFog: false // TODO fog not working properly
+        }).run()
+    }
+
+    await setAssembly(assembly)
+
+    return {
+        model,
+        get structure() { return structure },
+        cartoon,
+        ballAndStick,
+
+        setAssembly
+    }
+}
+
+// // create new structure via query
+// const q1 = Q.generators.atoms({
+//     residueTest: qtx => SP.residue.label_seq_id(qtx.element) < 7
+// });
+// const newStructure = StructureSelection.unionStructure(await StructureQuery.run(q1, structure));
+
+// // ball+stick for new structure
+// const newBallStickRepr = BallAndStickRepresentation()
+// await newBallStickRepr.create(newStructure, {
+//     colorTheme: { name: 'element-symbol' },
+//     sizeTheme: { name: 'uniform', value: 0.1 },
+//     useFog: false // TODO fog not working properly
+// }).run()
+// viewer.add(newBallStickRepr)
+
+// // create a mesh
+// const meshBuilder = MeshBuilder.create(256, 128)
+// const colors: Color[] = []
+// const labels: string[] = []
+// // red sphere
+// meshBuilder.setGroup(0)
+// colors[0] = Color(0xFF2233)
+// labels[0] = 'red sphere'
+// addSphere(meshBuilder, Vec3.create(0, 0, 0), 4, 2)
+// // green cube
+// meshBuilder.setGroup(1)
+// colors[1] = Color(0x2233FF)
+// labels[1] = 'blue cube'
+// const t = Mat4.identity()
+// Mat4.fromTranslation(t, Vec3.create(10, 0, 0))
+// Mat4.scale(t, t, Vec3.create(3, 3, 3))
+// meshBuilder.add(t, Box())
+// const mesh = meshBuilder.getMesh()
+// const mesh = getObjFromUrl('mesh.obj')
+
+// // create shape from mesh
+// const shape = Shape.create('myShape', mesh, colors, labels)
+
+// // add representation from shape
+// const customRepr = ShapeRepresentation()
+// await customRepr.create(shape, {
+//     colorTheme: { name: 'shape-group' },
+//     // colorTheme: { name: 'uniform', value: Color(0xFFCC22) },
+//     useFog: false // TODO fog not working properly
+// }).run()
+// viewer.add(customRepr)

+ 3 - 0
src/mol-geo/representation/shape/index.ts

@@ -28,6 +28,9 @@ export const DefaultShapeProps = {
 }
 export type ShapeProps = typeof DefaultShapeProps
 
+// TODO
+// export type ShapeRepresentation = ShapeRepresentation<ShapeProps>
+
 export function ShapeRepresentation<P extends ShapeProps>(): ShapeRepresentation<P> {
     const renderObjects: RenderObject[] = []
     let _renderObject: MeshRenderObject

+ 3 - 1
src/mol-geo/representation/structure/representation/backbone.ts

@@ -17,7 +17,9 @@ export const DefaultBackboneProps = {
 }
 export type BackboneProps = typeof DefaultBackboneProps
 
-export function BackboneRepresentation(): StructureRepresentation<BackboneProps> {
+export type BackboneRepresentation = StructureRepresentation<BackboneProps>
+
+export function BackboneRepresentation(): BackboneRepresentation {
     const traceRepr = UnitsRepresentation(PolymerBackboneVisual)
 
     let currentProps: BackboneProps

+ 3 - 1
src/mol-geo/representation/structure/representation/ball-and-stick.ts

@@ -24,7 +24,9 @@ export const DefaultBallAndStickProps = {
 }
 export type BallAndStickProps = typeof DefaultBallAndStickProps
 
-export function BallAndStickRepresentation(): StructureRepresentation<BallAndStickProps> {
+export type BallAndStickRepresentation = StructureRepresentation<BallAndStickProps>
+
+export function BallAndStickRepresentation(): BallAndStickRepresentation {
     const elmementRepr = UnitsRepresentation(ElementSphereVisual)
     const intraLinkRepr = UnitsRepresentation(IntraUnitLinkVisual)
     const interLinkRepr = ComplexRepresentation(InterUnitLinkVisual)

+ 3 - 1
src/mol-geo/representation/structure/representation/carbohydrate.ts

@@ -19,7 +19,9 @@ export const DefaultCartoonProps = {
 }
 export type CarbohydrateProps = typeof DefaultCartoonProps
 
-export function CarbohydrateRepresentation(): StructureRepresentation<CarbohydrateProps> {
+export type CarbohydrateRepresentation = StructureRepresentation<CarbohydrateProps>
+
+export function CarbohydrateRepresentation(): CarbohydrateRepresentation {
     const carbohydrateSymbolRepr = ComplexRepresentation(CarbohydrateSymbolVisual)
     const carbohydrateLinkRepr = ComplexRepresentation(CarbohydrateLinkVisual)
 

+ 3 - 1
src/mol-geo/representation/structure/representation/cartoon.ts

@@ -23,7 +23,9 @@ export const DefaultCartoonProps = {
 }
 export type CartoonProps = typeof DefaultCartoonProps
 
-export function CartoonRepresentation(): StructureRepresentation<CartoonProps> {
+export type CartoonRepresentation = StructureRepresentation<CartoonProps>
+
+export function CartoonRepresentation(): CartoonRepresentation {
     const traceRepr = UnitsRepresentation(PolymerTraceVisual)
     const gapRepr = UnitsRepresentation(PolymerGapVisual)
     const blockRepr = UnitsRepresentation(NucleotideBlockVisual)

+ 3 - 1
src/mol-geo/representation/structure/representation/distance-restraint.ts

@@ -19,7 +19,9 @@ export const DefaultDistanceRestraintProps = {
 }
 export type DistanceRestraintProps = typeof DefaultDistanceRestraintProps
 
-export function DistanceRestraintRepresentation(): StructureRepresentation<DistanceRestraintProps> {
+export type DistanceRestraintRepresentation = StructureRepresentation<DistanceRestraintProps>
+
+export function DistanceRestraintRepresentation(): DistanceRestraintRepresentation {
     const crossLinkRepr = ComplexRepresentation(CrossLinkRestraintVisual)
 
     let currentProps: DistanceRestraintProps

+ 4 - 1
src/mol-geo/representation/structure/representation/spacefill.ts

@@ -6,12 +6,15 @@
 
 import { UnitsRepresentation } from '..';
 import { ElementSphereVisual, DefaultElementSphereProps } from '../visual/element-sphere';
+import { StructureRepresentation } from '../units-representation';
 
 export const DefaultSpacefillProps = {
     ...DefaultElementSphereProps
 }
 export type SpacefillProps = typeof DefaultSpacefillProps
 
-export function SpacefillRepresentation() {
+export type SpacefillRepresentation = StructureRepresentation<SpacefillProps>
+
+export function SpacefillRepresentation(): SpacefillRepresentation {
     return UnitsRepresentation(ElementSphereVisual)
 }