nucleotide-block-mesh.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  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 { Unit } from 'mol-model/structure';
  7. import { UnitsVisual } from '..';
  8. import { RuntimeContext } from 'mol-task'
  9. import { Mesh } from '../../../shape/mesh';
  10. import { MeshBuilder } from '../../../shape/mesh-builder';
  11. import { getElementLoci, markElement } from './util/element';
  12. import { Vec3, Mat4 } from 'mol-math/linear-algebra';
  13. import { Segmentation, SortedArray } from 'mol-data/int';
  14. import { MoleculeType, isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types';
  15. import { getElementIndexForAtomId, getElementIndexForAtomRole } from 'mol-model/structure/util';
  16. import { StructureElementIterator } from './util/location-iterator';
  17. import { DefaultUnitsMeshProps, UnitsMeshVisual } from '../units-visual';
  18. const p1 = Vec3.zero()
  19. const p2 = Vec3.zero()
  20. const p3 = Vec3.zero()
  21. const p4 = Vec3.zero()
  22. const p5 = Vec3.zero()
  23. const p6 = Vec3.zero()
  24. const v12 = Vec3.zero()
  25. const v34 = Vec3.zero()
  26. const vC = Vec3.zero()
  27. const center = Vec3.zero()
  28. const t = Mat4.identity()
  29. const sVec = Vec3.zero()
  30. async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, props: {}, mesh?: Mesh) {
  31. if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
  32. // TODO better vertex count estimate
  33. const builder = MeshBuilder.create(256, 128, mesh)
  34. const { elements, model } = unit
  35. const { chemicalComponentMap, modifiedResidues } = model.properties
  36. const { chainAtomSegments, residueAtomSegments, residues } = model.atomicHierarchy
  37. const { label_comp_id } = residues
  38. const pos = unit.conformation.invariantPosition
  39. const chainIt = Segmentation.transientSegments(chainAtomSegments, elements)
  40. const residueIt = Segmentation.transientSegments(residueAtomSegments, elements)
  41. let i = 0
  42. while (chainIt.hasNext) {
  43. residueIt.setSegment(chainIt.move());
  44. while (residueIt.hasNext) {
  45. const { index: residueIndex } = residueIt.move();
  46. const cc = chemicalComponentMap.get(label_comp_id.value(residueIndex))
  47. const moleculeType = cc ? cc.moleculeType : MoleculeType.unknown
  48. if (isNucleic(moleculeType)) {
  49. let compId = label_comp_id.value(residueIndex)
  50. const parentId = modifiedResidues.parentId.get(compId)
  51. if (parentId !== undefined) compId = parentId
  52. let idx1 = -1, idx2 = -1, idx3 = -1, idx4 = -1, idx5 = -1, idx6 = -1
  53. let width = 4.5, height = 4.5, depth = 0.5
  54. if (isPurinBase(compId)) {
  55. height = 4.5
  56. idx1 = getElementIndexForAtomId(model, residueIndex, 'N1')
  57. idx2 = getElementIndexForAtomId(model, residueIndex, 'C4')
  58. idx3 = getElementIndexForAtomId(model, residueIndex, 'C6')
  59. idx4 = getElementIndexForAtomId(model, residueIndex, 'C2')
  60. idx5 = getElementIndexForAtomId(model, residueIndex, 'N9')
  61. idx6 = getElementIndexForAtomRole(model, residueIndex, 'trace')
  62. } else if (isPyrimidineBase(compId)) {
  63. height = 3.0
  64. idx1 = getElementIndexForAtomId(model, residueIndex, 'N3')
  65. idx2 = getElementIndexForAtomId(model, residueIndex, 'C6')
  66. idx3 = getElementIndexForAtomId(model, residueIndex, 'C4')
  67. idx4 = getElementIndexForAtomId(model, residueIndex, 'C2')
  68. idx5 = getElementIndexForAtomId(model, residueIndex, 'N1')
  69. idx6 = getElementIndexForAtomRole(model, residueIndex, 'trace')
  70. }
  71. if (idx1 !== -1 && idx2 !== -1 && idx3 !== -1 && idx4 !== -1 && idx5 !== -1 && idx6 !== -1) {
  72. pos(idx1, p1); pos(idx2, p2); pos(idx3, p3); pos(idx4, p4); pos(idx5, p5); pos(idx6, p6)
  73. Vec3.normalize(v12, Vec3.sub(v12, p2, p1))
  74. Vec3.normalize(v34, Vec3.sub(v34, p4, p3))
  75. Vec3.normalize(vC, Vec3.cross(vC, v12, v34))
  76. Mat4.targetTo(t, p1, p2, vC)
  77. Vec3.scaleAndAdd(center, p1, v12, height / 2 - 0.2)
  78. Mat4.scale(t, t, Vec3.set(sVec, width, depth, height))
  79. Mat4.setTranslation(t, center)
  80. builder.setId(SortedArray.findPredecessorIndex(elements, idx6))
  81. builder.addBox(t)
  82. builder.addCylinder(p5, p6, 1, { radiusTop: 0.2, radiusBottom: 0.2 })
  83. }
  84. }
  85. if (i % 10000 === 0 && ctx.shouldUpdate) {
  86. await ctx.update({ message: 'Nucleotide block mesh', current: i });
  87. }
  88. ++i
  89. }
  90. }
  91. return builder.getMesh()
  92. }
  93. export const DefaultNucleotideBlockProps = {
  94. ...DefaultUnitsMeshProps
  95. }
  96. export type NucleotideBlockProps = typeof DefaultNucleotideBlockProps
  97. export function NucleotideBlockVisual(): UnitsVisual<NucleotideBlockProps> {
  98. return UnitsMeshVisual<NucleotideBlockProps>({
  99. defaultProps: DefaultNucleotideBlockProps,
  100. createMesh: createNucleotideBlockMesh,
  101. createLocationIterator: StructureElementIterator.fromGroup,
  102. getLoci: getElementLoci,
  103. mark: markElement,
  104. setUpdateState: () => {}
  105. })
  106. }