index.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import './index.html'
  7. import Viewer from 'mol-view/viewer';
  8. import CIF, { CifBlock } from 'mol-io/reader/cif'
  9. // import { parse as parseObj } from 'mol-io/reader/obj/parser'
  10. import { readUrlAs } from 'mol-util/read'
  11. import { Model, Format, Structure, StructureSymmetry } from 'mol-model/structure';
  12. import { CartoonRepresentation } from 'mol-geo/representation/structure/representation/cartoon';
  13. import { BallAndStickRepresentation } from 'mol-geo/representation/structure/representation/ball-and-stick';
  14. import { EveryLoci } from 'mol-model/loci';
  15. import { MarkerAction } from 'mol-geo/util/marker-data';
  16. import { labelFirst } from 'mol-view/label';
  17. // import { Queries as Q, StructureProperties as SP, StructureSelection, StructureQuery } from 'mol-model/structure';
  18. import { MeshBuilder } from 'mol-geo/mesh/mesh-builder';
  19. import { ShapeRepresentation } from 'mol-geo/representation/shape';
  20. import { /*Vec3, Mat4,*/ Tensor } from 'mol-math/linear-algebra';
  21. import { Shape } from 'mol-model/shape';
  22. import { Color } from 'mol-util/color';
  23. import { addSphere } from 'mol-geo/mesh/builder/sphere';
  24. // import { Box } from 'mol-geo/primitive/box';
  25. import { AssemblySymmetry } from 'mol-model-props/rcsb/symmetry';
  26. import { addCylinder } from 'mol-geo/mesh/builder/cylinder';
  27. import { Table } from 'mol-data/db';
  28. const container = document.getElementById('container')
  29. if (!container) throw new Error('Can not find element with id "container".')
  30. const canvas = document.getElementById('canvas') as HTMLCanvasElement
  31. if (!canvas) throw new Error('Can not find element with id "canvas".')
  32. const info = document.getElementById('info') as HTMLCanvasElement
  33. if (!info) throw new Error('Can not find element with id "info".')
  34. const viewer = Viewer.create(canvas, container)
  35. viewer.animate()
  36. viewer.input.resize.subscribe(() => {
  37. // do whatever appropriate
  38. })
  39. viewer.input.move.subscribe(({x, y, inside, buttons}) => {
  40. if (!inside || buttons) return
  41. const p = viewer.identify(x, y)
  42. const loci = viewer.getLoci(p)
  43. viewer.mark(EveryLoci, MarkerAction.RemoveHighlight)
  44. viewer.mark(loci, MarkerAction.Highlight)
  45. const label = labelFirst(loci)
  46. info.innerText = `${label}`
  47. })
  48. // async function getObjFromUrl(url: string) {
  49. // const data = await readUrlAs(url, false) as string
  50. // const comp = parseObj(data)
  51. // const parsed = await comp.run()
  52. // if (parsed.isError) throw parsed
  53. // return parsed.result
  54. // }
  55. async function getCifFromUrl(url: string) {
  56. const data = await readUrlAs(url, false)
  57. const comp = CIF.parse(data)
  58. const parsed = await comp.run()
  59. if (parsed.isError) throw parsed
  60. return parsed.result.blocks[0]
  61. }
  62. async function getModelFromMmcif(cif: CifBlock) {
  63. const models = await Model.create(Format.mmCIF(cif)).run()
  64. return models[0]
  65. }
  66. async function getStructureFromModel(model: Model, assembly = '1') {
  67. const assemblies = model.symmetry.assemblies
  68. if (assemblies.length) {
  69. return await StructureSymmetry.buildAssembly(Structure.ofModel(model), assembly).run()
  70. } else {
  71. return Structure.ofModel(model)
  72. }
  73. }
  74. function getAxesShape(featureId: number, assemblySymmetry: AssemblySymmetry) {
  75. const f = assemblySymmetry.db.rcsb_assembly_symmetry_feature
  76. const feature = Table.pickRow(f, i => f.id.value(i) === featureId)
  77. if (!feature) return
  78. const axes = assemblySymmetry.getAxes(featureId)
  79. if (!axes._rowCount) return
  80. const vectorSpace = AssemblySymmetry.Schema.rcsb_assembly_symmetry_axis.start.space;
  81. const colors: Color[] = []
  82. const labels: string[] = []
  83. const radius = 0.4
  84. const cylinderProps = { radiusTop: radius, radiusBottom: radius }
  85. const meshBuilder = MeshBuilder.create(256, 128)
  86. for (let i = 0, il = axes._rowCount; i < il; ++i) {
  87. const start = Tensor.toVec3(vectorSpace, axes.start.value(i))
  88. const end = Tensor.toVec3(vectorSpace, axes.end.value(i))
  89. meshBuilder.setGroup(i)
  90. addSphere(meshBuilder, start, radius, 2)
  91. addSphere(meshBuilder, end, radius, 2)
  92. addCylinder(meshBuilder, start, end, 1, cylinderProps)
  93. colors.push(Color(0xCCEE11))
  94. labels.push(`Axis ${i + 1} for ${feature.symmetry_value} ${feature.type.toLowerCase()} symmetry`)
  95. }
  96. const mesh = meshBuilder.getMesh()
  97. const shape = Shape.create('Axes', mesh, colors, labels)
  98. return shape
  99. }
  100. function getClusterColorTheme(featureId: number, assemblySymmetry: AssemblySymmetry) {
  101. const f = assemblySymmetry.db.rcsb_assembly_symmetry_feature
  102. const feature = Table.pickRow(f, i => f.id.value(i) === featureId)
  103. if (!feature) return
  104. const clusters = assemblySymmetry.getClusters(featureId)
  105. if (!clusters._rowCount) return
  106. for (let i = 0, il = clusters._rowCount; i < il; ++i) {
  107. console.log(clusters.members.value(i), clusters.avg_rmsd.value(i), feature.stoichiometry_value, feature.stoichiometry_description)
  108. }
  109. }
  110. async function init() {
  111. const assembly = '1'
  112. const cif = await getCifFromUrl('https://files.rcsb.org/download/4hhb.cif')
  113. const model = await getModelFromMmcif(cif)
  114. const structure = await getStructureFromModel(model, assembly)
  115. viewer.center(structure.boundary.sphere.center)
  116. // cartoon for whole structure
  117. const cartoonRepr = CartoonRepresentation()
  118. await cartoonRepr.create(structure, {
  119. colorTheme: { name: 'chain-id' },
  120. sizeTheme: { name: 'uniform', value: 0.2 },
  121. useFog: false // TODO fog not working properly
  122. }).run()
  123. viewer.add(cartoonRepr)
  124. // ball+stick for whole structure
  125. const ballStickRepr = BallAndStickRepresentation()
  126. await ballStickRepr.create(structure, {
  127. colorTheme: { name: 'element-symbol' },
  128. sizeTheme: { name: 'uniform', value: 0.1 },
  129. useFog: false // TODO fog not working properly
  130. }).run()
  131. viewer.add(ballStickRepr)
  132. // // create new structure via query
  133. // const q1 = Q.generators.atoms({
  134. // residueTest: qtx => SP.residue.label_seq_id(qtx.element) < 7
  135. // });
  136. // const newStructure = StructureSelection.unionStructure(await StructureQuery.run(q1, structure));
  137. // // ball+stick for new structure
  138. // const newBallStickRepr = BallAndStickRepresentation()
  139. // await newBallStickRepr.create(newStructure, {
  140. // colorTheme: { name: 'element-symbol' },
  141. // sizeTheme: { name: 'uniform', value: 0.1 },
  142. // useFog: false // TODO fog not working properly
  143. // }).run()
  144. // viewer.add(newBallStickRepr)
  145. // // create a mesh
  146. // const meshBuilder = MeshBuilder.create(256, 128)
  147. // const colors: Color[] = []
  148. // const labels: string[] = []
  149. // // red sphere
  150. // meshBuilder.setGroup(0)
  151. // colors[0] = Color(0xFF2233)
  152. // labels[0] = 'red sphere'
  153. // addSphere(meshBuilder, Vec3.create(0, 0, 0), 4, 2)
  154. // // green cube
  155. // meshBuilder.setGroup(1)
  156. // colors[1] = Color(0x2233FF)
  157. // labels[1] = 'blue cube'
  158. // const t = Mat4.identity()
  159. // Mat4.fromTranslation(t, Vec3.create(10, 0, 0))
  160. // Mat4.scale(t, t, Vec3.create(3, 3, 3))
  161. // meshBuilder.add(t, Box())
  162. // const mesh = meshBuilder.getMesh()
  163. // const mesh = getObjFromUrl('mesh.obj')
  164. // // create shape from mesh
  165. // const shape = Shape.create('myShape', mesh, colors, labels)
  166. // // add representation from shape
  167. // const customRepr = ShapeRepresentation()
  168. // await customRepr.create(shape, {
  169. // colorTheme: { name: 'shape-group' },
  170. // // colorTheme: { name: 'uniform', value: Color(0xFFCC22) },
  171. // useFog: false // TODO fog not working properly
  172. // }).run()
  173. // viewer.add(customRepr)
  174. await AssemblySymmetry.attachFromCifOrAPI(model)
  175. const assemblySymmetry = AssemblySymmetry.get(model)
  176. console.log(assemblySymmetry)
  177. if (assemblySymmetry) {
  178. const features = assemblySymmetry.getFeatures(assembly)
  179. if (features._rowCount) {
  180. const axesShape = getAxesShape(features.id.value(1), assemblySymmetry)
  181. console.log(axesShape)
  182. if (axesShape) {
  183. const customRepr = ShapeRepresentation()
  184. await customRepr.create(axesShape, {
  185. colorTheme: { name: 'shape-group' },
  186. // colorTheme: { name: 'uniform', value: Color(0xFFCC22) },
  187. useFog: false // TODO fog not working properly
  188. }).run()
  189. viewer.add(customRepr)
  190. }
  191. getClusterColorTheme(features.id.value(0), assemblySymmetry)
  192. getClusterColorTheme(features.id.value(1), assemblySymmetry)
  193. }
  194. }
  195. // ensure the added representations get rendered, i.e. without mouse input
  196. viewer.requestDraw()
  197. }
  198. init()