element-sphere.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  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, Element } from 'mol-model/structure';
  10. import { DefaultStructureProps, UnitsVisual } from '../index';
  11. import { RuntimeContext } from 'mol-task'
  12. import { createTransforms, createColors, createElementSphereMesh, markElement, getElementRadius } from '../utils';
  13. import { deepEqual, defaults } from 'mol-util';
  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 { OrderedSet } from 'mol-data/int';
  20. import { createMarkers, MarkerAction } from '../../../util/marker-data';
  21. import { Loci, EmptyLoci } from 'mol-model/loci';
  22. import { SizeTheme } from '../../../theme';
  23. export const DefaultElementSphereProps = {
  24. ...DefaultStructureProps,
  25. sizeTheme: { name: 'physical', factor: 1 } as SizeTheme,
  26. flipSided: false,
  27. flatShaded: false,
  28. detail: 0,
  29. }
  30. export type ElementSphereProps = Partial<typeof DefaultElementSphereProps>
  31. export function ElementSphereVisual(): UnitsVisual<ElementSphereProps> {
  32. const renderObjects: RenderObject[] = []
  33. let spheres: MeshRenderObject
  34. let currentProps: typeof DefaultElementSphereProps
  35. let mesh: Mesh
  36. let currentGroup: Unit.SymmetryGroup
  37. return {
  38. renderObjects,
  39. async create(ctx: RuntimeContext, group: Unit.SymmetryGroup, props: ElementSphereProps = {}) {
  40. currentProps = Object.assign({}, DefaultElementSphereProps, props)
  41. renderObjects.length = 0 // clear
  42. currentGroup = group
  43. const { detail, colorTheme, sizeTheme } = { ...DefaultElementSphereProps, ...props }
  44. const instanceCount = group.units.length
  45. const elementCount = group.elements.length
  46. const unit = group.units[0]
  47. const radius = getElementRadius(unit, sizeTheme)
  48. mesh = await createElementSphereMesh(ctx, unit, radius, detail, mesh)
  49. if (ctx.shouldUpdate) await ctx.update('Computing spacefill transforms');
  50. const transforms = createTransforms(group)
  51. if (ctx.shouldUpdate) await ctx.update('Computing spacefill colors');
  52. const color = createColors(group, elementCount, colorTheme)
  53. if (ctx.shouldUpdate) await ctx.update('Computing spacefill marks');
  54. const marker = createMarkers(instanceCount * elementCount)
  55. const values: MeshValues = {
  56. ...getMeshData(mesh),
  57. aTransform: transforms,
  58. aInstanceId: ValueCell.create(fillSerial(new Float32Array(instanceCount))),
  59. ...color,
  60. ...marker,
  61. uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
  62. uInstanceCount: ValueCell.create(instanceCount),
  63. uElementCount: ValueCell.create(elementCount),
  64. elements: mesh.indexBuffer,
  65. drawCount: ValueCell.create(mesh.triangleCount * 3),
  66. instanceCount: ValueCell.create(instanceCount),
  67. dDoubleSided: ValueCell.create(defaults(props.doubleSided, true)),
  68. dFlatShaded: ValueCell.create(defaults(props.flatShaded, false)),
  69. dFlipSided: ValueCell.create(defaults(props.flipSided, false)),
  70. dUseFog: ValueCell.create(defaults(props.useFog, true)),
  71. }
  72. const state: RenderableState = {
  73. depthMask: defaults(props.depthMask, true),
  74. visible: defaults(props.visible, true)
  75. }
  76. spheres = createMeshRenderObject(values, state)
  77. renderObjects.push(spheres)
  78. },
  79. async update(ctx: RuntimeContext, props: ElementSphereProps) {
  80. const newProps = Object.assign({}, currentProps, props)
  81. if (!spheres) return false
  82. let updateColor = false
  83. if (newProps.detail !== currentProps.detail) {
  84. const unit = currentGroup.units[0]
  85. const radius = getElementRadius(unit, newProps.sizeTheme)
  86. mesh = await createElementSphereMesh(ctx, unit, radius, newProps.detail, mesh)
  87. ValueCell.update(spheres.values.drawCount, mesh.triangleCount * 3)
  88. updateColor = true
  89. }
  90. if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) {
  91. updateColor = true
  92. }
  93. if (updateColor) {
  94. const elementCount = currentGroup.elements.length
  95. if (ctx.shouldUpdate) await ctx.update('Computing spacefill colors');
  96. createColors(currentGroup, elementCount, newProps.colorTheme, spheres.values)
  97. }
  98. ValueCell.updateIfChanged(spheres.values.uAlpha, newProps.alpha)
  99. ValueCell.updateIfChanged(spheres.values.dDoubleSided, newProps.doubleSided)
  100. ValueCell.updateIfChanged(spheres.values.dFlipSided, newProps.flipSided)
  101. ValueCell.updateIfChanged(spheres.values.dFlatShaded, newProps.flatShaded)
  102. spheres.state.visible = newProps.visible
  103. spheres.state.depthMask = newProps.depthMask
  104. currentProps = newProps
  105. return true
  106. },
  107. getLoci(pickingId: PickingId) {
  108. const { objectId, instanceId, elementId } = pickingId
  109. if (spheres.id === objectId) {
  110. const unit = currentGroup.units[instanceId]
  111. const indices = OrderedSet.ofSingleton(elementId as Element.Index);
  112. return Element.Loci([{ unit, indices }])
  113. }
  114. return EmptyLoci
  115. },
  116. mark(loci: Loci, action: MarkerAction) {
  117. markElement(spheres.values.tMarker, currentGroup, loci, action)
  118. },
  119. destroy() {
  120. // TODO
  121. }
  122. }
  123. }