element-point.ts 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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 { PointRenderObject } from 'mol-gl/render-object'
  9. import { Unit, Structure } from 'mol-model/structure';
  10. import { RuntimeContext } from 'mol-task'
  11. import { UnitsVisual, DefaultStructureProps } from '..';
  12. import { getElementLoci, StructureElementIterator, markElement } from './util/element';
  13. import { createColors, createSizes, createUnitsPointRenderObject } from './util/common';
  14. import { deepEqual, UUID } from 'mol-util';
  15. import { Interval } from 'mol-data/int';
  16. import { PickingId } from '../../../util/picking';
  17. import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
  18. import { MarkerAction, createMarkers, applyMarkerAction } from '../../../util/marker-data';
  19. import { Vec3 } from 'mol-math/linear-algebra';
  20. import { fillSerial } from 'mol-util/array';
  21. import { SizeThemeProps } from 'mol-view/theme/size';
  22. import { LocationIterator } from '../../../util/location-iterator';
  23. import { createTransforms } from '../../../util/transform-data';
  24. import { StructureGroup } from '../units-visual';
  25. import { updateRenderableState, updateBaseValues } from '../../util';
  26. export const DefaultElementPointProps = {
  27. ...DefaultStructureProps,
  28. sizeTheme: { name: 'uniform', value: 0.2 } as SizeThemeProps,
  29. pointSizeAttenuation: true,
  30. }
  31. export type ElementPointProps = Partial<typeof DefaultElementPointProps>
  32. export async function createElementPointVertices(ctx: RuntimeContext, unit: Unit, vertices?: ValueCell<Float32Array>) {
  33. const elements = unit.elements
  34. const n = elements.length * 3
  35. const array = vertices && vertices.ref.value.length >= n ? vertices.ref.value : new Float32Array(n)
  36. const pos = unit.conformation.invariantPosition
  37. const p = Vec3.zero()
  38. for (let i = 0; i < n; i += 3) {
  39. pos(elements[i / 3], p)
  40. array[i] = p[0]
  41. array[i + 1] = p[1]
  42. array[i + 2] = p[2]
  43. if (i % 10000 === 0 && ctx.shouldUpdate) {
  44. await ctx.update({ message: 'Creating points', current: i / 3, max: elements.length });
  45. }
  46. ++i
  47. }
  48. return vertices ? ValueCell.update(vertices, array) : ValueCell.create(array)
  49. }
  50. export function ElementPointVisual(): UnitsVisual<ElementPointProps> {
  51. let renderObject: PointRenderObject | undefined
  52. let currentProps = DefaultElementPointProps
  53. let currentGroup: Unit.SymmetryGroup
  54. let currentStructure: Structure
  55. let locationIt: LocationIterator
  56. let vertices: ValueCell<Float32Array>
  57. let currentConformationId: UUID
  58. return {
  59. get renderObject () { return renderObject },
  60. async createOrUpdate(ctx: RuntimeContext, props: ElementPointProps = {}, structureGroup?: StructureGroup) {
  61. if (structureGroup) currentStructure = structureGroup.structure
  62. const group = structureGroup ? structureGroup.group : undefined
  63. if (!group && !currentGroup) {
  64. throw new Error('missing group')
  65. } else if (group && !currentGroup) {
  66. currentProps = Object.assign({}, DefaultElementPointProps, props)
  67. currentProps.colorTheme.structure = currentStructure
  68. currentGroup = group
  69. locationIt = StructureElementIterator.fromGroup(group)
  70. const unit = group.units[0]
  71. currentConformationId = Unit.conformationId(unit)
  72. vertices = await createElementPointVertices(ctx, unit, vertices)
  73. renderObject = await createUnitsPointRenderObject(ctx, group, vertices, locationIt, currentProps)
  74. } else if (renderObject) {
  75. if (group) currentGroup = group
  76. const newProps = { ...currentProps, ...props }
  77. const unit = currentGroup.units[0]
  78. let updateTransform = false
  79. let createVertices = false
  80. let updateColor = false
  81. let updateSize = false
  82. const newConformationId = Unit.conformationId(unit)
  83. if (newConformationId !== currentConformationId) {
  84. currentConformationId = newConformationId
  85. createVertices = true
  86. }
  87. if (currentGroup.units.length !== locationIt.instanceCount) updateTransform = true
  88. if (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) createVertices = true
  89. if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) updateColor = true
  90. if (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) updateSize = true
  91. if (updateTransform) {
  92. locationIt = StructureElementIterator.fromGroup(currentGroup)
  93. const { instanceCount, groupCount } = locationIt
  94. createTransforms(currentGroup, renderObject.values)
  95. createMarkers(instanceCount * groupCount, renderObject.values)
  96. updateColor = true
  97. updateSize = true
  98. }
  99. if (createVertices) {
  100. await createElementPointVertices(ctx, unit, vertices)
  101. ValueCell.update(renderObject.values.aGroup, fillSerial(new Float32Array(locationIt.groupCount))) // TODO reuse array
  102. ValueCell.update(renderObject.values.drawCount, locationIt.groupCount)
  103. updateColor = true
  104. updateSize = true
  105. }
  106. if (updateColor) {
  107. await createColors(ctx, locationIt, newProps.colorTheme, renderObject.values)
  108. }
  109. if (updateSize) {
  110. await createSizes(ctx, locationIt, newProps.sizeTheme, renderObject.values)
  111. }
  112. updateBaseValues(renderObject.values, newProps)
  113. updateRenderableState(renderObject.state, newProps)
  114. currentProps = newProps
  115. }
  116. },
  117. getLoci(pickingId: PickingId) {
  118. return renderObject ? getElementLoci(pickingId, currentGroup, renderObject.id) : EmptyLoci
  119. },
  120. mark(loci: Loci, action: MarkerAction) {
  121. if (!renderObject) return false
  122. const { tMarker } = renderObject.values
  123. const { groupCount, instanceCount } = locationIt
  124. function apply(interval: Interval) {
  125. const start = Interval.start(interval)
  126. const end = Interval.end(interval)
  127. return applyMarkerAction(tMarker.ref.value.array, start, end, action)
  128. }
  129. let changed = false
  130. if (isEveryLoci(loci)) {
  131. apply(Interval.ofBounds(0, groupCount * instanceCount))
  132. changed = true
  133. } else {
  134. changed = markElement(loci, currentGroup, apply)
  135. }
  136. if (changed) {
  137. ValueCell.update(tMarker, tMarker.ref.value)
  138. }
  139. return changed
  140. },
  141. destroy() {
  142. // TODO
  143. renderObject = undefined
  144. }
  145. }
  146. }