nucleotide-block-mesh.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /**
  2. * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { ParamDefinition as PD } from '../../../mol-util/param-definition';
  7. import { Vec3, Mat4 } from '../../../mol-math/linear-algebra';
  8. import { Box } from '../../../mol-geo/primitive/box';
  9. import { VisualContext } from '../../visual';
  10. import { Unit, Structure, ElementIndex } from '../../../mol-model/structure';
  11. import { Theme } from '../../../mol-theme/theme';
  12. import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
  13. import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder';
  14. import { Segmentation } from '../../../mol-data/int';
  15. import { CylinderProps } from '../../../mol-geo/primitive/cylinder';
  16. import { isNucleic, isPurineBase, isPyrimidineBase } from '../../../mol-model/structure/model/types';
  17. import { addCylinder } from '../../../mol-geo/geometry/mesh/builder/cylinder';
  18. import { UnitsMeshParams, UnitsVisual, UnitsMeshVisual } from '../units-visual';
  19. import { NucleotideLocationIterator, getNucleotideElementLoci, eachNucleotideElement } from './util/nucleotide';
  20. import { VisualUpdateState } from '../../util';
  21. const p1 = Vec3.zero()
  22. const p2 = Vec3.zero()
  23. const p3 = Vec3.zero()
  24. const p4 = Vec3.zero()
  25. const p5 = Vec3.zero()
  26. const p6 = Vec3.zero()
  27. const v12 = Vec3.zero()
  28. const v34 = Vec3.zero()
  29. const vC = Vec3.zero()
  30. const center = Vec3.zero()
  31. const t = Mat4.identity()
  32. const sVec = Vec3.zero()
  33. const box = Box()
  34. export const NucleotideBlockMeshParams = {
  35. sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }),
  36. radialSegments: PD.Numeric(16, { min: 2, max: 56, step: 2 }),
  37. }
  38. export const DefaultNucleotideBlockMeshProps = PD.getDefaultValues(NucleotideBlockMeshParams)
  39. export type NucleotideBlockMeshProps = typeof DefaultNucleotideBlockMeshProps
  40. function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: NucleotideBlockMeshProps, mesh?: Mesh) {
  41. if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
  42. const nucleotideElementCount = unit.nucleotideElements.length
  43. if (!nucleotideElementCount) return Mesh.createEmpty(mesh)
  44. const { sizeFactor, radialSegments } = props
  45. const vertexCount = nucleotideElementCount * (box.vertices.length / 3 + radialSegments * 2)
  46. const builderState = MeshBuilder.createState(vertexCount, vertexCount / 4, mesh)
  47. const { elements, model } = unit
  48. const { chainAtomSegments, residueAtomSegments, residues, index: atomicIndex } = model.atomicHierarchy
  49. const { moleculeType, traceElementIndex } = model.atomicHierarchy.derived.residue
  50. const { label_comp_id } = residues
  51. const pos = unit.conformation.invariantPosition
  52. const chainIt = Segmentation.transientSegments(chainAtomSegments, elements)
  53. const residueIt = Segmentation.transientSegments(residueAtomSegments, elements)
  54. const cylinderProps: CylinderProps = { radiusTop: 1 * sizeFactor, radiusBottom: 1 * sizeFactor, radialSegments, bottomCap: true }
  55. let i = 0
  56. while (chainIt.hasNext) {
  57. residueIt.setSegment(chainIt.move());
  58. while (residueIt.hasNext) {
  59. const { index: residueIndex } = residueIt.move();
  60. if (isNucleic(moleculeType[residueIndex])) {
  61. const compId = label_comp_id.value(residueIndex)
  62. let idx1: ElementIndex | -1 = -1, idx2: ElementIndex | -1 = -1, idx3: ElementIndex | -1 = -1, idx4: ElementIndex | -1 = -1, idx5: ElementIndex | -1 = -1, idx6: ElementIndex | -1 = -1
  63. let width = 4.5, height = 4.5, depth = 2.5 * sizeFactor
  64. let isPurine = isPurineBase(compId)
  65. let isPyrimidine = isPyrimidineBase(compId)
  66. if (!isPurine && !isPyrimidine) {
  67. // detect Purine or Pyrimidin based on geometry
  68. const idxC4 = atomicIndex.findAtomOnResidue(residueIndex, 'C4')
  69. const idxN9 = atomicIndex.findAtomOnResidue(residueIndex, 'N9')
  70. if (idxC4 !== -1 && idxN9 !== -1 && Vec3.distance(pos(idxC4, p1), pos(idxN9, p2)) < 1.6) {
  71. isPurine = true
  72. } else {
  73. isPyrimidine = true
  74. }
  75. }
  76. if (isPurine) {
  77. height = 4.5
  78. idx1 = atomicIndex.findAtomOnResidue(residueIndex, 'N1')
  79. idx2 = atomicIndex.findAtomOnResidue(residueIndex, 'C4')
  80. idx3 = atomicIndex.findAtomOnResidue(residueIndex, 'C6')
  81. idx4 = atomicIndex.findAtomOnResidue(residueIndex, 'C2')
  82. idx5 = atomicIndex.findAtomOnResidue(residueIndex, 'N9')
  83. idx6 = traceElementIndex[residueIndex]
  84. } else if (isPyrimidine) {
  85. height = 3.0
  86. idx1 = atomicIndex.findAtomOnResidue(residueIndex, 'N3')
  87. idx2 = atomicIndex.findAtomOnResidue(residueIndex, 'C6')
  88. idx3 = atomicIndex.findAtomOnResidue(residueIndex, 'C4')
  89. idx4 = atomicIndex.findAtomOnResidue(residueIndex, 'C2')
  90. idx5 = atomicIndex.findAtomOnResidue(residueIndex, 'N1')
  91. if (idx5 === -1) {
  92. // modified ring, e.g. DZ
  93. idx5 = atomicIndex.findAtomOnResidue(residueIndex, 'C1')
  94. }
  95. idx6 = traceElementIndex[residueIndex]
  96. }
  97. if (idx5 !== -1 && idx6 !== -1) {
  98. pos(idx5, p5); pos(idx6, p6)
  99. builderState.currentGroup = i
  100. addCylinder(builderState, p5, p6, 1, cylinderProps)
  101. if (idx1 !== -1 && idx2 !== -1 && idx3 !== -1 && idx4 !== -1) {
  102. pos(idx1, p1); pos(idx2, p2); pos(idx3, p3); pos(idx4, p4);
  103. Vec3.normalize(v12, Vec3.sub(v12, p2, p1))
  104. Vec3.normalize(v34, Vec3.sub(v34, p4, p3))
  105. Vec3.normalize(vC, Vec3.cross(vC, v12, v34))
  106. Mat4.targetTo(t, p1, p2, vC)
  107. Vec3.scaleAndAdd(center, p1, v12, height / 2 - 0.2)
  108. Mat4.scale(t, t, Vec3.set(sVec, width, depth, height))
  109. Mat4.setTranslation(t, center)
  110. MeshBuilder.addPrimitive(builderState, t, box)
  111. }
  112. }
  113. ++i
  114. }
  115. }
  116. }
  117. return MeshBuilder.getMesh(builderState)
  118. }
  119. export const NucleotideBlockParams = {
  120. ...UnitsMeshParams,
  121. ...NucleotideBlockMeshParams
  122. }
  123. export type NucleotideBlockParams = typeof NucleotideBlockParams
  124. export function NucleotideBlockVisual(materialId: number): UnitsVisual<NucleotideBlockParams> {
  125. return UnitsMeshVisual<NucleotideBlockParams>({
  126. defaultProps: PD.getDefaultValues(NucleotideBlockParams),
  127. createGeometry: createNucleotideBlockMesh,
  128. createLocationIterator: NucleotideLocationIterator.fromGroup,
  129. getLoci: getNucleotideElementLoci,
  130. eachLocation: eachNucleotideElement,
  131. setUpdateState: (state: VisualUpdateState, newProps: PD.Values<NucleotideBlockParams>, currentProps: PD.Values<NucleotideBlockParams>) => {
  132. state.createGeometry = (
  133. newProps.sizeFactor !== currentProps.sizeFactor ||
  134. newProps.radialSegments !== currentProps.radialSegments
  135. )
  136. }
  137. }, materialId)
  138. }