inter-unit-link-cylinder.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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 { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
  8. import { Link, Structure } from 'mol-model/structure';
  9. import { DefaultStructureProps, StructureVisual } from '../index';
  10. import { RuntimeContext } from 'mol-task'
  11. import { LinkCylinderProps, DefaultLinkCylinderProps, createLinkCylinderMesh } from './util/link';
  12. import { MeshValues } from 'mol-gl/renderable';
  13. import { getMeshData } from '../../../util/mesh-data';
  14. import { Mesh } from '../../../shape/mesh';
  15. import { PickingId } from '../../../util/picking';
  16. import { Vec3 } from 'mol-math/linear-algebra';
  17. import { createUniformColor } from '../../../util/color-data';
  18. import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
  19. import { MarkerAction, applyMarkerAction, createMarkers, MarkerData } from '../../../util/marker-data';
  20. import { SizeTheme } from '../../../theme';
  21. import { createIdentityTransform } from './util/common';
  22. import { updateMeshValues, updateRenderableState, createMeshValues, createRenderableState } from '../../util';
  23. // import { chainIdLinkColorData } from '../../../theme/structure/color/chain-id';
  24. async function createInterUnitLinkCylinderMesh(ctx: RuntimeContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
  25. const links = structure.links
  26. const { bondCount, bonds } = links
  27. if (!bondCount) return Mesh.createEmpty(mesh)
  28. const builderProps = {
  29. linkCount: bondCount,
  30. referencePosition: (edgeIndex: number) => null, // TODO
  31. position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
  32. const b = bonds[edgeIndex]
  33. const uA = b.unitA, uB = b.unitB
  34. uA.conformation.position(uA.elements[b.indexA], posA)
  35. uB.conformation.position(uB.elements[b.indexB], posB)
  36. },
  37. order: (edgeIndex: number) => bonds[edgeIndex].order,
  38. flags: (edgeIndex: number) => bonds[edgeIndex].flag
  39. }
  40. return createLinkCylinderMesh(ctx, builderProps, props, mesh)
  41. }
  42. export const DefaultInterUnitLinkProps = {
  43. ...DefaultStructureProps,
  44. ...DefaultLinkCylinderProps,
  45. sizeTheme: { name: 'physical', factor: 0.3 } as SizeTheme,
  46. }
  47. export type InterUnitLinkProps = Partial<typeof DefaultInterUnitLinkProps>
  48. export function InterUnitLinkVisual(): StructureVisual<InterUnitLinkProps> {
  49. const renderObjects: RenderObject[] = []
  50. let cylinders: MeshRenderObject
  51. let currentProps: typeof DefaultInterUnitLinkProps
  52. let mesh: Mesh
  53. let currentStructure: Structure
  54. return {
  55. renderObjects,
  56. async create(ctx: RuntimeContext, structure: Structure, props: InterUnitLinkProps = {}) {
  57. currentProps = Object.assign({}, DefaultInterUnitLinkProps, props)
  58. renderObjects.length = 0 // clear
  59. currentStructure = structure
  60. const elementCount = structure.links.bondCount
  61. const instanceCount = 1
  62. mesh = await createInterUnitLinkCylinderMesh(ctx, structure, currentProps)
  63. if (ctx.shouldUpdate) await ctx.update('Computing link transforms');
  64. const transforms = createIdentityTransform()
  65. if (ctx.shouldUpdate) await ctx.update('Computing link colors');
  66. const color = createUniformColor({ value: 0x999911 })
  67. // const color = chainIdLinkColorData({ group, elementCount })
  68. if (ctx.shouldUpdate) await ctx.update('Computing link marks');
  69. const marker = createMarkers(instanceCount * elementCount)
  70. const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
  71. const values: MeshValues = {
  72. ...getMeshData(mesh),
  73. ...color,
  74. ...marker,
  75. aTransform: transforms,
  76. elements: mesh.indexBuffer,
  77. ...createMeshValues(currentProps, counts),
  78. }
  79. const state = createRenderableState(currentProps)
  80. cylinders = createMeshRenderObject(values, state)
  81. renderObjects.push(cylinders)
  82. },
  83. async update(ctx: RuntimeContext, props: InterUnitLinkProps) {
  84. const newProps = Object.assign({}, currentProps, props)
  85. if (!cylinders) return false
  86. // TODO create in-place
  87. if (currentProps.radialSegments !== newProps.radialSegments) return false
  88. updateMeshValues(cylinders.values, newProps)
  89. updateRenderableState(cylinders.state, newProps)
  90. return false
  91. },
  92. getLoci(pickingId: PickingId) {
  93. return getLinkLoci(pickingId, currentStructure, cylinders.id)
  94. },
  95. mark(loci: Loci, action: MarkerAction) {
  96. markLink(loci, action, currentStructure, cylinders.values)
  97. },
  98. destroy() {
  99. // TODO
  100. }
  101. }
  102. }
  103. function getLinkLoci(pickingId: PickingId, structure: Structure, id: number) {
  104. const { objectId, elementId } = pickingId
  105. if (id === objectId) {
  106. const bond = structure.links.bonds[elementId]
  107. return Link.Loci([{
  108. aUnit: bond.unitA,
  109. aIndex: bond.indexA,
  110. bUnit: bond.unitB,
  111. bIndex: bond.indexB
  112. }])
  113. }
  114. return EmptyLoci
  115. }
  116. function markLink(loci: Loci, action: MarkerAction, structure: Structure, values: MarkerData) {
  117. const tMarker = values.tMarker
  118. const links = structure.links
  119. const elementCount = links.bondCount
  120. const instanceCount = 1
  121. let changed = false
  122. const array = tMarker.ref.value.array
  123. if (isEveryLoci(loci)) {
  124. applyMarkerAction(array, 0, elementCount * instanceCount, action)
  125. changed = true
  126. } else if (Link.isLoci(loci)) {
  127. for (const b of loci.links) {
  128. const _idx = structure.links.getBondIndex(b.aIndex, b.aUnit, b.bIndex, b.bUnit)
  129. if (_idx !== -1) {
  130. const idx = _idx
  131. if (applyMarkerAction(array, idx, idx + 1, action) && !changed) {
  132. changed = true
  133. }
  134. }
  135. }
  136. } else {
  137. return
  138. }
  139. if (changed) {
  140. ValueCell.update(tMarker, tMarker.ref.value)
  141. }
  142. }