nucleotide-block-mesh.ts 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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 { ValueCell } from 'mol-util/value-cell'
  7. import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
  8. import { Unit } from 'mol-model/structure';
  9. import { DefaultStructureProps, UnitsVisual } from '..';
  10. import { RuntimeContext } from 'mol-task'
  11. import { createTransforms, createColors } from './util/common';
  12. import { deepEqual } from 'mol-util';
  13. import { MeshValues } from 'mol-gl/renderable';
  14. import { getMeshData } from '../../../util/mesh-data';
  15. import { Mesh } from '../../../shape/mesh';
  16. import { PickingId } from '../../../util/picking';
  17. import { createMarkers, MarkerAction } from '../../../util/marker-data';
  18. import { Loci } from 'mol-model/loci';
  19. import { SizeTheme } from '../../../theme';
  20. import { createMeshValues, updateMeshValues, updateRenderableState, createRenderableState, DefaultMeshProps } from '../../util';
  21. import { MeshBuilder } from '../../../shape/mesh-builder';
  22. import { getElementLoci, markElement } from './util/element';
  23. import { Vec3, Mat4 } from 'mol-math/linear-algebra';
  24. import { Segmentation, SortedArray } from 'mol-data/int';
  25. import { MoleculeType, isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types';
  26. import { getElementIndexForAtomId, getElementIndexForAtomRole } from 'mol-model/structure/util';
  27. const p1 = Vec3.zero()
  28. const p2 = Vec3.zero()
  29. const p3 = Vec3.zero()
  30. const p4 = Vec3.zero()
  31. const p5 = Vec3.zero()
  32. const p6 = Vec3.zero()
  33. const v12 = Vec3.zero()
  34. const v34 = Vec3.zero()
  35. const vC = Vec3.zero()
  36. const center = Vec3.zero()
  37. const t = Mat4.identity()
  38. const sVec = Vec3.zero()
  39. async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, mesh?: Mesh) {
  40. if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
  41. const builder = MeshBuilder.create(256, 128, mesh)
  42. const { elements, model } = unit
  43. const { chemicalComponentMap, modifiedResidues } = model.properties
  44. const { chainAtomSegments, residueAtomSegments, residues } = model.atomicHierarchy
  45. const { label_comp_id } = residues
  46. const pos = unit.conformation.invariantPosition
  47. const chainIt = Segmentation.transientSegments(chainAtomSegments, elements)
  48. const residueIt = Segmentation.transientSegments(residueAtomSegments, elements)
  49. let i = 0
  50. while (chainIt.hasNext) {
  51. residueIt.setSegment(chainIt.move());
  52. while (residueIt.hasNext) {
  53. const { index: residueIndex } = residueIt.move();
  54. const cc = chemicalComponentMap.get(label_comp_id.value(residueIndex))
  55. const moleculeType = cc ? cc.moleculeType : MoleculeType.unknown
  56. if (isNucleic(moleculeType)) {
  57. let compId = label_comp_id.value(residueIndex)
  58. const parentId = modifiedResidues.parentId.get(compId)
  59. if (parentId !== undefined) compId = parentId
  60. let idx1 = -1, idx2 = -1, idx3 = -1, idx4 = -1, idx5 = -1, idx6 = -1
  61. let width = 4.5, height = 4.5, depth = 0.5
  62. if (isPurinBase(compId)) {
  63. height = 4.5
  64. idx1 = getElementIndexForAtomId(model, residueIndex, 'N1')
  65. idx2 = getElementIndexForAtomId(model, residueIndex, 'C4')
  66. idx3 = getElementIndexForAtomId(model, residueIndex, 'C6')
  67. idx4 = getElementIndexForAtomId(model, residueIndex, 'C2')
  68. idx5 = getElementIndexForAtomId(model, residueIndex, 'N9')
  69. idx6 = getElementIndexForAtomRole(model, residueIndex, 'trace')
  70. } else if (isPyrimidineBase(compId)) {
  71. height = 3.0
  72. idx1 = getElementIndexForAtomId(model, residueIndex, 'N3')
  73. idx2 = getElementIndexForAtomId(model, residueIndex, 'C6')
  74. idx3 = getElementIndexForAtomId(model, residueIndex, 'C4')
  75. idx4 = getElementIndexForAtomId(model, residueIndex, 'C2')
  76. idx5 = getElementIndexForAtomId(model, residueIndex, 'N1')
  77. idx6 = getElementIndexForAtomRole(model, residueIndex, 'trace')
  78. }
  79. if (idx1 !== -1 && idx2 !== -1 && idx3 !== -1 && idx4 !== -1 && idx5 !== -1 && idx6 !== -1) {
  80. pos(idx1, p1); pos(idx2, p2); pos(idx3, p3); pos(idx4, p4); pos(idx5, p5); pos(idx6, p6)
  81. Vec3.normalize(v12, Vec3.sub(v12, p2, p1))
  82. Vec3.normalize(v34, Vec3.sub(v34, p4, p3))
  83. Vec3.normalize(vC, Vec3.cross(vC, v12, v34))
  84. Mat4.targetTo(t, p1, p2, vC)
  85. Vec3.scaleAndAdd(center, p1, v12, height / 2 - 0.2)
  86. Mat4.scale(t, t, Vec3.set(sVec, width, depth, height))
  87. Mat4.setTranslation(t, center)
  88. builder.setId(SortedArray.findPredecessorIndex(elements, idx6))
  89. builder.addBox(t)
  90. builder.addCylinder(p5, p6, 1, { radiusTop: 0.2, radiusBottom: 0.2 })
  91. }
  92. }
  93. if (i % 10000 === 0 && ctx.shouldUpdate) {
  94. await ctx.update({ message: 'Gap mesh', current: i });
  95. }
  96. ++i
  97. }
  98. }
  99. return builder.getMesh()
  100. }
  101. export const DefaultNucleotideBlockProps = {
  102. ...DefaultMeshProps,
  103. ...DefaultStructureProps,
  104. sizeTheme: { name: 'physical', factor: 1 } as SizeTheme,
  105. detail: 0,
  106. unitKinds: [ Unit.Kind.Atomic, Unit.Kind.Spheres ] as Unit.Kind[]
  107. }
  108. export type NucleotideBlockProps = Partial<typeof DefaultNucleotideBlockProps>
  109. export function NucleotideBlockVisual(): UnitsVisual<NucleotideBlockProps> {
  110. let renderObject: MeshRenderObject
  111. let currentProps: typeof DefaultNucleotideBlockProps
  112. let mesh: Mesh
  113. let currentGroup: Unit.SymmetryGroup
  114. return {
  115. get renderObject () { return renderObject },
  116. async create(ctx: RuntimeContext, group: Unit.SymmetryGroup, props: NucleotideBlockProps = {}) {
  117. currentProps = Object.assign({}, DefaultNucleotideBlockProps, props)
  118. currentGroup = group
  119. const { colorTheme, unitKinds } = { ...DefaultNucleotideBlockProps, ...props }
  120. const instanceCount = group.units.length
  121. const elementCount = group.elements.length
  122. const unit = group.units[0]
  123. mesh = unitKinds.includes(unit.kind)
  124. ? await createNucleotideBlockMesh(ctx, unit, mesh)
  125. : Mesh.createEmpty(mesh)
  126. // console.log(mesh)
  127. const transforms = createTransforms(group)
  128. const color = createColors(group, elementCount, colorTheme)
  129. const marker = createMarkers(instanceCount * elementCount)
  130. const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
  131. const values: MeshValues = {
  132. ...getMeshData(mesh),
  133. ...color,
  134. ...marker,
  135. aTransform: transforms,
  136. elements: mesh.indexBuffer,
  137. ...createMeshValues(currentProps, counts),
  138. aColor: ValueCell.create(new Float32Array(mesh.vertexCount * 3))
  139. }
  140. const state = createRenderableState(currentProps)
  141. renderObject = createMeshRenderObject(values, state)
  142. },
  143. async update(ctx: RuntimeContext, props: NucleotideBlockProps) {
  144. const newProps = Object.assign({}, currentProps, props)
  145. if (!renderObject) return false
  146. let updateColor = false
  147. if (newProps.detail !== currentProps.detail) {
  148. const unit = currentGroup.units[0]
  149. mesh = await createNucleotideBlockMesh(ctx, unit, mesh)
  150. ValueCell.update(renderObject.values.drawCount, mesh.triangleCount * 3)
  151. updateColor = true
  152. }
  153. if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) {
  154. updateColor = true
  155. }
  156. if (updateColor) {
  157. const elementCount = currentGroup.elements.length
  158. if (ctx.shouldUpdate) await ctx.update('Computing nucleotide block colors');
  159. createColors(currentGroup, elementCount, newProps.colorTheme, renderObject.values)
  160. }
  161. updateMeshValues(renderObject.values, newProps)
  162. updateRenderableState(renderObject.state, newProps)
  163. currentProps = newProps
  164. return true
  165. },
  166. getLoci(pickingId: PickingId) {
  167. return getElementLoci(renderObject.id, currentGroup, pickingId)
  168. },
  169. mark(loci: Loci, action: MarkerAction) {
  170. markElement(renderObject.values.tMarker, currentGroup, loci, action)
  171. },
  172. destroy() {
  173. // TODO
  174. }
  175. }
  176. }