intra-unit-link-cylinder.ts 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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. // TODO multiple cylinders for higher bond orders
  8. import { ValueCell } from 'mol-util/value-cell'
  9. import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
  10. import { Unit, Link } from 'mol-model/structure';
  11. import { UnitsVisual, DefaultStructureProps } from '../index';
  12. import { RuntimeContext } from 'mol-task'
  13. import { createTransforms } from '../utils';
  14. import { fillSerial } from 'mol-gl/renderable/util';
  15. import { RenderableState, MeshValues } from 'mol-gl/renderable';
  16. import { getMeshData } from '../../../util/mesh-data';
  17. import { Mesh } from '../../../shape/mesh';
  18. import { PickingId } from '../../../util/picking';
  19. import { MeshBuilder } from '../../../shape/mesh-builder';
  20. import { Vec3, Mat4 } from 'mol-math/linear-algebra';
  21. import { createUniformColor } from '../../../util/color-data';
  22. import { defaults } from 'mol-util';
  23. import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
  24. import { MarkerAction, applyMarkerAction, createMarkers, MarkerData } from '../../../util/marker-data';
  25. import { SizeTheme } from '../../../theme';
  26. async function createLinkCylinderMesh(ctx: RuntimeContext, unit: Unit, mesh?: Mesh) {
  27. if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
  28. const elements = unit.elements;
  29. const links = unit.links
  30. const { edgeCount, a, b } = links
  31. if (!edgeCount) return Mesh.createEmpty(mesh)
  32. // TODO calculate vertextCount properly
  33. const vertexCount = 32 * edgeCount
  34. const meshBuilder = MeshBuilder.create(vertexCount, vertexCount / 2, mesh)
  35. const va = Vec3.zero()
  36. const vb = Vec3.zero()
  37. const vt = Vec3.zero()
  38. const m = Mat4.identity()
  39. const pos = unit.conformation.invariantPosition
  40. // const l = Element.Location()
  41. // l.unit = unit
  42. for (let edgeIndex = 0, _eI = edgeCount * 2; edgeIndex < _eI; ++edgeIndex) {
  43. const aI = elements[a[edgeIndex]], bI = elements[b[edgeIndex]];
  44. // each edge is included twice because of the "adjacency list" structure
  45. // keep only the 1st occurence.
  46. if (aI >= bI) continue;
  47. pos(aI, va)
  48. pos(bI, vb)
  49. Vec3.scale(vt, Vec3.add(vt, va, vb), 0.5)
  50. Vec3.makeRotation(m, Vec3.create(0, 1, 0), Vec3.sub(vb, vb, va))
  51. Mat4.setTranslation(m, vt)
  52. meshBuilder.setId(edgeIndex)
  53. meshBuilder.addCylinder(m, { radiusTop: 0.2, radiusBottom: 0.2 })
  54. if (edgeIndex % 10000 === 0 && ctx.shouldUpdate) {
  55. await ctx.update({ message: 'Cylinder mesh', current: edgeIndex, max: edgeCount });
  56. }
  57. }
  58. return meshBuilder.getMesh()
  59. }
  60. export const DefaultIntraUnitLinkProps = {
  61. ...DefaultStructureProps,
  62. sizeTheme: { name: 'physical', factor: 0.3 } as SizeTheme,
  63. flipSided: false,
  64. flatShaded: false,
  65. }
  66. export type IntraUnitLinkProps = Partial<typeof DefaultIntraUnitLinkProps>
  67. export function IntraUnitLinkVisual(): UnitsVisual<IntraUnitLinkProps> {
  68. const renderObjects: RenderObject[] = []
  69. let cylinders: MeshRenderObject
  70. let currentProps: typeof DefaultIntraUnitLinkProps
  71. let mesh: Mesh
  72. let currentGroup: Unit.SymmetryGroup
  73. // let vertexMap: VertexMap
  74. return {
  75. renderObjects,
  76. async create(ctx: RuntimeContext, group: Unit.SymmetryGroup, props: IntraUnitLinkProps = {}) {
  77. currentProps = Object.assign({}, DefaultIntraUnitLinkProps, props)
  78. renderObjects.length = 0 // clear
  79. currentGroup = group
  80. const unit = group.units[0]
  81. const elementCount = Unit.isAtomic(unit) ? unit.links.edgeCount * 2 : 0
  82. const instanceCount = group.units.length
  83. mesh = await createLinkCylinderMesh(ctx, unit)
  84. // console.log(mesh)
  85. // vertexMap = VertexMap.fromMesh(mesh)
  86. if (ctx.shouldUpdate) await ctx.update('Computing link transforms');
  87. const transforms = createTransforms(group)
  88. if (ctx.shouldUpdate) await ctx.update('Computing link colors');
  89. const color = createUniformColor({ value: 0xFF0000 })
  90. if (ctx.shouldUpdate) await ctx.update('Computing link marks');
  91. const marker = createMarkers(instanceCount * elementCount)
  92. const values: MeshValues = {
  93. ...getMeshData(mesh),
  94. aTransform: transforms,
  95. aInstanceId: ValueCell.create(fillSerial(new Float32Array(instanceCount))),
  96. ...color,
  97. ...marker,
  98. uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
  99. uInstanceCount: ValueCell.create(instanceCount),
  100. uElementCount: ValueCell.create(elementCount),
  101. elements: mesh.indexBuffer,
  102. drawCount: ValueCell.create(mesh.triangleCount * 3),
  103. instanceCount: ValueCell.create(instanceCount),
  104. dDoubleSided: ValueCell.create(defaults(props.doubleSided, true)),
  105. dFlatShaded: ValueCell.create(defaults(props.flatShaded, false)),
  106. dFlipSided: ValueCell.create(defaults(props.flipSided, false)),
  107. dUseFog: ValueCell.create(defaults(props.useFog, true)),
  108. }
  109. const state: RenderableState = {
  110. depthMask: defaults(props.depthMask, true),
  111. visible: defaults(props.visible, true)
  112. }
  113. cylinders = createMeshRenderObject(values, state)
  114. renderObjects.push(cylinders)
  115. },
  116. async update(ctx: RuntimeContext, props: IntraUnitLinkProps) {
  117. const newProps = Object.assign({}, currentProps, props)
  118. if (!cylinders) return false
  119. // TODO
  120. ValueCell.updateIfChanged(cylinders.values.uAlpha, newProps.alpha)
  121. ValueCell.updateIfChanged(cylinders.values.dDoubleSided, newProps.doubleSided)
  122. ValueCell.updateIfChanged(cylinders.values.dFlipSided, newProps.flipSided)
  123. ValueCell.updateIfChanged(cylinders.values.dFlatShaded, newProps.flatShaded)
  124. cylinders.state.visible = newProps.visible
  125. cylinders.state.depthMask = newProps.depthMask
  126. return true
  127. },
  128. getLoci(pickingId: PickingId) {
  129. return getLinkLoci(pickingId, currentGroup, cylinders.id)
  130. },
  131. mark(loci: Loci, action: MarkerAction) {
  132. markLink(loci, action, currentGroup, cylinders.values)
  133. },
  134. destroy() {
  135. // TODO
  136. }
  137. }
  138. }
  139. function getLinkLoci(pickingId: PickingId, group: Unit.SymmetryGroup, id: number) {
  140. const { objectId, instanceId, elementId } = pickingId
  141. const unit = group.units[instanceId]
  142. if (id === objectId && Unit.isAtomic(unit)) {
  143. return Link.Loci([{
  144. aUnit: unit,
  145. aIndex: unit.links.a[elementId],
  146. bUnit: unit,
  147. bIndex: unit.links.b[elementId]
  148. }])
  149. }
  150. return EmptyLoci
  151. }
  152. function markLink(loci: Loci, action: MarkerAction, group: Unit.SymmetryGroup, values: MarkerData) {
  153. const tMarker = values.tMarker
  154. const unit = group.units[0]
  155. if (!Unit.isAtomic(unit)) return
  156. const elementCount = unit.links.edgeCount * 2
  157. const instanceCount = group.units.length
  158. let changed = false
  159. const array = tMarker.ref.value.array
  160. if (isEveryLoci(loci)) {
  161. applyMarkerAction(array, 0, elementCount * instanceCount, action)
  162. changed = true
  163. } else if (Link.isLoci(loci)) {
  164. for (const b of loci.links) {
  165. const unitIdx = Unit.findUnitById(b.aUnit.id, group.units)
  166. if (unitIdx !== -1) {
  167. const _idx = unit.links.getEdgeIndex(b.aIndex, b.bIndex)
  168. if (_idx !== -1) {
  169. const idx = _idx
  170. if (applyMarkerAction(array, idx, idx + 1, action) && !changed) {
  171. changed = true
  172. }
  173. }
  174. }
  175. }
  176. } else {
  177. return
  178. }
  179. if (changed) {
  180. ValueCell.update(tMarker, tMarker.ref.value)
  181. }
  182. }