intra-unit-link-cylinder.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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. * @author David Sehnal <david.sehnal@gmail.com>
  6. */
  7. import { ValueCell } from 'mol-util/value-cell'
  8. import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
  9. import { Unit, Link } from 'mol-model/structure';
  10. import { UnitsVisual, DefaultStructureProps } from '../index';
  11. import { RuntimeContext } from 'mol-task'
  12. import { DefaultLinkCylinderProps, LinkCylinderProps, createLinkCylinderMesh } from './util/link';
  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 { Vec3 } from 'mol-math/linear-algebra';
  18. // import { createUniformColor } from '../../../util/color-data';
  19. import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
  20. import { MarkerAction, applyMarkerAction, createMarkers, MarkerData } from '../../../util/marker-data';
  21. import { SizeTheme } from '../../../theme';
  22. import { chainIdLinkColorData } from '../../../theme/structure/color/chain-id';
  23. import { createTransforms } from './util/common';
  24. import { createMeshValues, createRenderableState, updateMeshValues, updateRenderableState } from '../../util';
  25. async function createIntraUnitLinkCylinderMesh(ctx: RuntimeContext, unit: Unit, props: LinkCylinderProps, mesh?: Mesh) {
  26. if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
  27. const elements = unit.elements;
  28. const links = unit.links
  29. const { edgeCount, a, b, edgeProps, offset } = links
  30. const { order: _order, flags: _flags } = edgeProps
  31. if (!edgeCount) return Mesh.createEmpty(mesh)
  32. const vRef = Vec3.zero()
  33. const pos = unit.conformation.invariantPosition
  34. const builderProps = {
  35. linkCount: edgeCount * 2,
  36. referencePosition: (edgeIndex: number) => {
  37. let aI = a[edgeIndex], bI = b[edgeIndex];
  38. if (aI > bI) [aI, bI] = [bI, aI]
  39. for (let i = offset[aI], il = offset[aI + 1]; i < il; ++i) {
  40. if (b[i] !== bI) return pos(elements[b[i]], vRef)
  41. }
  42. for (let i = offset[bI], il = offset[bI + 1]; i < il; ++i) {
  43. if (a[i] !== aI) return pos(elements[a[i]], vRef)
  44. }
  45. return null
  46. },
  47. position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
  48. pos(elements[a[edgeIndex]], posA)
  49. pos(elements[b[edgeIndex]], posB)
  50. },
  51. order: (edgeIndex: number) => _order[edgeIndex],
  52. flags: (edgeIndex: number) => _flags[edgeIndex]
  53. }
  54. return createLinkCylinderMesh(ctx, builderProps, props, mesh)
  55. }
  56. export const DefaultIntraUnitLinkProps = {
  57. ...DefaultStructureProps,
  58. ...DefaultLinkCylinderProps,
  59. sizeTheme: { name: 'physical', factor: 0.3 } as SizeTheme,
  60. }
  61. export type IntraUnitLinkProps = Partial<typeof DefaultIntraUnitLinkProps>
  62. export function IntraUnitLinkVisual(): UnitsVisual<IntraUnitLinkProps> {
  63. const renderObjects: RenderObject[] = []
  64. let cylinders: MeshRenderObject
  65. let currentProps: typeof DefaultIntraUnitLinkProps
  66. let mesh: Mesh
  67. let currentGroup: Unit.SymmetryGroup
  68. return {
  69. renderObjects,
  70. async create(ctx: RuntimeContext, group: Unit.SymmetryGroup, props: IntraUnitLinkProps = {}) {
  71. currentProps = Object.assign({}, DefaultIntraUnitLinkProps, props)
  72. renderObjects.length = 0 // clear
  73. currentGroup = group
  74. const unit = group.units[0]
  75. const elementCount = Unit.isAtomic(unit) ? unit.links.edgeCount * 2 : 0
  76. const instanceCount = group.units.length
  77. mesh = await createIntraUnitLinkCylinderMesh(ctx, unit, currentProps)
  78. if (ctx.shouldUpdate) await ctx.update('Computing link transforms');
  79. const transforms = createTransforms(group)
  80. if (ctx.shouldUpdate) await ctx.update('Computing link colors');
  81. // const color = createUniformColor({ value: 0xFF0000 })
  82. const color = chainIdLinkColorData({ group, elementCount })
  83. if (ctx.shouldUpdate) await ctx.update('Computing link marks');
  84. const marker = createMarkers(instanceCount * elementCount)
  85. const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
  86. const values: MeshValues = {
  87. ...getMeshData(mesh),
  88. ...color,
  89. ...marker,
  90. aTransform: transforms,
  91. elements: mesh.indexBuffer,
  92. ...createMeshValues(currentProps, counts),
  93. }
  94. const state = createRenderableState(currentProps)
  95. cylinders = createMeshRenderObject(values, state)
  96. renderObjects.push(cylinders)
  97. },
  98. async update(ctx: RuntimeContext, props: IntraUnitLinkProps) {
  99. const newProps = Object.assign({}, currentProps, props)
  100. if (!cylinders || currentProps.radialSegments !== newProps.radialSegments) return false
  101. // TODO
  102. updateMeshValues(cylinders.values, newProps)
  103. updateRenderableState(cylinders.state, newProps)
  104. return true
  105. },
  106. getLoci(pickingId: PickingId) {
  107. return getLinkLoci(pickingId, currentGroup, cylinders.id)
  108. },
  109. mark(loci: Loci, action: MarkerAction) {
  110. markLink(loci, action, currentGroup, cylinders.values)
  111. },
  112. destroy() {
  113. // TODO
  114. }
  115. }
  116. }
  117. function getLinkLoci(pickingId: PickingId, group: Unit.SymmetryGroup, id: number) {
  118. const { objectId, instanceId, elementId } = pickingId
  119. const unit = group.units[instanceId]
  120. if (id === objectId && Unit.isAtomic(unit)) {
  121. return Link.Loci([{
  122. aUnit: unit,
  123. aIndex: unit.links.a[elementId],
  124. bUnit: unit,
  125. bIndex: unit.links.b[elementId]
  126. }])
  127. }
  128. return EmptyLoci
  129. }
  130. function markLink(loci: Loci, action: MarkerAction, group: Unit.SymmetryGroup, values: MarkerData) {
  131. const tMarker = values.tMarker
  132. const unit = group.units[0]
  133. if (!Unit.isAtomic(unit)) return
  134. const elementCount = unit.links.edgeCount * 2
  135. const instanceCount = group.units.length
  136. let changed = false
  137. const array = tMarker.ref.value.array
  138. if (isEveryLoci(loci)) {
  139. applyMarkerAction(array, 0, elementCount * instanceCount, action)
  140. changed = true
  141. } else if (Link.isLoci(loci)) {
  142. for (const b of loci.links) {
  143. const unitIdx = Unit.findUnitById(b.aUnit.id, group.units)
  144. if (unitIdx !== -1) {
  145. const _idx = unit.links.getDirectedEdgeIndex(b.aIndex, b.bIndex)
  146. if (_idx !== -1) {
  147. const idx = _idx
  148. if (applyMarkerAction(array, idx, idx + 1, action) && !changed) {
  149. changed = true
  150. }
  151. }
  152. }
  153. }
  154. } else {
  155. return
  156. }
  157. if (changed) {
  158. ValueCell.update(tMarker, tMarker.ref.value)
  159. }
  160. }