point.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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 { createPointRenderObject, RenderObject, PointRenderObject } from 'mol-gl/render-object'
  9. import { Unit, Element } from 'mol-model/structure';
  10. import { Task } from 'mol-task'
  11. import { fillSerial } from 'mol-gl/renderable/util';
  12. import { UnitsRepresentation, DefaultStructureProps } from './index';
  13. import VertexMap from '../../shape/vertex-map';
  14. import { SizeTheme } from '../../theme';
  15. import { createTransforms, createColors, createSizes, applyElementFlags } from './utils';
  16. import { deepEqual, defaults } from 'mol-util';
  17. import { SortedArray } from 'mol-data/int';
  18. import { RenderableState, PointValues } from 'mol-gl/renderable';
  19. import { PickingId } from '../../util/picking';
  20. import { Loci } from 'mol-model/loci';
  21. import { FlagAction, createFlags } from '../../util/flag-data';
  22. export const DefaultPointProps = {
  23. ...DefaultStructureProps,
  24. sizeTheme: { name: 'vdw' } as SizeTheme
  25. }
  26. export type PointProps = Partial<typeof DefaultPointProps>
  27. export function createPointVertices(unit: Unit) {
  28. const elements = unit.elements
  29. const elementCount = elements.length
  30. const vertices = new Float32Array(elementCount * 3)
  31. const { x, y, z } = unit.conformation
  32. const l = Element.Location()
  33. l.unit = unit
  34. for (let i = 0; i < elementCount; i++) {
  35. l.element = elements[i];
  36. const i3 = i * 3
  37. vertices[i3] = x(l.element)
  38. vertices[i3 + 1] = y(l.element)
  39. vertices[i3 + 2] = z(l.element)
  40. }
  41. return vertices
  42. }
  43. export default function PointUnitsRepresentation(): UnitsRepresentation<PointProps> {
  44. const renderObjects: RenderObject[] = []
  45. let points: PointRenderObject
  46. let currentProps = DefaultPointProps
  47. let currentGroup: Unit.SymmetryGroup
  48. let _units: ReadonlyArray<Unit>
  49. let _elements: SortedArray
  50. return {
  51. renderObjects,
  52. create(group: Unit.SymmetryGroup, props: PointProps = {}) {
  53. currentProps = Object.assign({}, DefaultPointProps, props)
  54. return Task.create('Point.create', async ctx => {
  55. renderObjects.length = 0 // clear
  56. currentGroup = group
  57. _units = group.units
  58. _elements = group.elements;
  59. const { colorTheme, sizeTheme } = currentProps
  60. const elementCount = _elements.length
  61. const instanceCount = group.units.length
  62. const vertexMap = VertexMap.create(
  63. elementCount,
  64. elementCount + 1,
  65. fillSerial(new Uint32Array(elementCount)),
  66. fillSerial(new Uint32Array(elementCount + 1))
  67. )
  68. await ctx.update('Computing point vertices');
  69. const vertices = createPointVertices(_units[0])
  70. await ctx.update('Computing point transforms');
  71. const transforms = createTransforms(group)
  72. await ctx.update('Computing point colors');
  73. const color = createColors(group, vertexMap, colorTheme)
  74. await ctx.update('Computing point sizes');
  75. const size = createSizes(group, vertexMap, sizeTheme)
  76. await ctx.update('Computing spacefill flags');
  77. const flag = createFlags(instanceCount * elementCount)
  78. const values: PointValues = {
  79. aPosition: ValueCell.create(vertices),
  80. aElementId: ValueCell.create(fillSerial(new Float32Array(elementCount))),
  81. aTransform: transforms,
  82. aInstanceId: ValueCell.create(fillSerial(new Float32Array(instanceCount))),
  83. ...color,
  84. ...flag,
  85. ...size,
  86. uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
  87. uInstanceCount: ValueCell.create(instanceCount),
  88. uElementCount: ValueCell.create(group.elements.length),
  89. drawCount: ValueCell.create(vertices.length / 3),
  90. instanceCount: ValueCell.create(instanceCount),
  91. dPointSizeAttenuation: ValueCell.create(true)
  92. }
  93. const state: RenderableState = {
  94. depthMask: defaults(props.depthMask, true),
  95. visible: defaults(props.visible, true)
  96. }
  97. points = createPointRenderObject(values, state)
  98. renderObjects.push(points)
  99. })
  100. },
  101. update(props: PointProps) {
  102. return Task.create('Point.update', async ctx => {
  103. if (!points || !_units || !_elements) return false
  104. const newProps = { ...currentProps, ...props }
  105. if (deepEqual(currentProps, newProps)) {
  106. console.log('props identical, nothing to change')
  107. return true
  108. }
  109. // const elementCount = OrderedSet.size(_elementGroup.elements)
  110. // const unitCount = _units.length
  111. // const vertexMap = VertexMap.create(
  112. // elementCount,
  113. // elementCount + 1,
  114. // fillSerial(new Uint32Array(elementCount)),
  115. // fillSerial(new Uint32Array(elementCount + 1))
  116. // )
  117. if (!deepEqual(currentProps.colorTheme, newProps.colorTheme)) {
  118. console.log('colorTheme changed', currentProps.colorTheme, newProps.colorTheme)
  119. // await ctx.update('Computing point colors');
  120. // const color = createColors(_units, _elementGroup, vertexMap, newProps.colorTheme)
  121. // ValueCell.update(points.props.color, color)
  122. }
  123. if (!deepEqual(currentProps.sizeTheme, newProps.sizeTheme)) {
  124. console.log('sizeTheme changed', currentProps.sizeTheme, newProps.sizeTheme)
  125. }
  126. currentProps = newProps
  127. return false
  128. })
  129. },
  130. getLoci(pickingId: PickingId) {
  131. const { objectId, instanceId, elementId } = pickingId
  132. if (points.id === objectId) {
  133. const unit = currentGroup.units[instanceId]
  134. const indices = SortedArray.ofSingleton(elementId)
  135. return Element.Loci([{ unit, indices }])
  136. }
  137. return null
  138. },
  139. applyFlags(loci: Loci, action: FlagAction) {
  140. applyElementFlags(points.values.tFlag, currentGroup, loci, action)
  141. }
  142. }
  143. }