index.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  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 { Structure, StructureSymmetry, Unit } from 'mol-model/structure';
  8. import { Task } from 'mol-task'
  9. import { RenderObject } from 'mol-gl/render-object';
  10. import { Representation, RepresentationProps, Visual } from '..';
  11. import { ColorTheme, SizeTheme } from '../../theme';
  12. import { PickingId } from '../../util/picking';
  13. import { Loci, EmptyLoci, isEmptyLoci } from 'mol-model/loci';
  14. import { MarkerAction } from '../../util/marker-data';
  15. export interface UnitsVisual<P extends RepresentationProps = {}> extends Visual<Unit.SymmetryGroup, P> { }
  16. export interface StructureVisual<P extends RepresentationProps = {}> extends Visual<Structure, P> { }
  17. export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P> { }
  18. export const DefaultStructureProps = {
  19. colorTheme: { name: 'instance-index' } as ColorTheme,
  20. sizeTheme: { name: 'physical' } as SizeTheme,
  21. alpha: 1,
  22. visible: true,
  23. doubleSided: false,
  24. depthMask: true,
  25. useFog: true,
  26. }
  27. export type StructureProps = Partial<typeof DefaultStructureProps>
  28. export function StructureRepresentation<P extends StructureProps>(unitsVisualCtor: () => UnitsVisual<P>, structureVisualCtor?: () => StructureVisual<P>): StructureRepresentation<P> {
  29. let unitsVisuals = new Map<number, { group: Unit.SymmetryGroup, visual: UnitsVisual<P> }>()
  30. let structureVisual: StructureVisual<P> | undefined
  31. let _props: Required<P>
  32. let _structure: Structure
  33. let _groups: ReadonlyArray<Unit.SymmetryGroup>
  34. function create(structure: Structure, props: P = {} as P) {
  35. _props = Object.assign({}, DefaultStructureProps, _props, props)
  36. return Task.create('Creating StructureRepresentation', async ctx => {
  37. if (!_structure) {
  38. _groups = structure.symmetryGroups;
  39. for (let i = 0; i < _groups.length; i++) {
  40. const group = _groups[i];
  41. const visual = unitsVisualCtor()
  42. await visual.create(ctx, group, _props)
  43. unitsVisuals.set(group.hashCode, { visual, group })
  44. }
  45. if (structureVisualCtor) {
  46. structureVisual = structureVisualCtor()
  47. await structureVisual.create(ctx, structure, _props)
  48. }
  49. } else {
  50. if (_structure.hashCode === structure.hashCode) {
  51. await update(_props)
  52. } else {
  53. _groups = structure.symmetryGroups;
  54. const newGroups: Unit.SymmetryGroup[] = []
  55. const oldUnitsVisuals = unitsVisuals
  56. unitsVisuals = new Map()
  57. for (let i = 0; i < _groups.length; i++) {
  58. const group = _groups[i];
  59. const visualGroup = oldUnitsVisuals.get(group.hashCode)
  60. if (visualGroup) {
  61. const { visual, group } = visualGroup
  62. if (!await visual.update(ctx, _props)) {
  63. await visual.create(ctx, group, _props)
  64. }
  65. oldUnitsVisuals.delete(group.hashCode)
  66. } else {
  67. newGroups.push(group)
  68. const visual = unitsVisualCtor()
  69. await visual.create(ctx, group, _props)
  70. unitsVisuals.set(group.hashCode, { visual, group })
  71. }
  72. }
  73. // for new groups, reuse leftover visuals
  74. const unusedVisuals: UnitsVisual<P>[] = []
  75. oldUnitsVisuals.forEach(({ visual }) => unusedVisuals.push(visual))
  76. newGroups.forEach(async group => {
  77. const visual = unusedVisuals.pop() || unitsVisualCtor()
  78. await visual.create(ctx, group, _props)
  79. unitsVisuals.set(group.hashCode, { visual, group })
  80. })
  81. unusedVisuals.forEach(visual => visual.destroy())
  82. if (structureVisual) {
  83. if (!await structureVisual.update(ctx, _props)) {
  84. await structureVisual.create(ctx, _structure, _props)
  85. }
  86. }
  87. }
  88. }
  89. _structure = structure
  90. });
  91. }
  92. function update(props: P) {
  93. return Task.create('Updating StructureRepresentation', async ctx => {
  94. _props = Object.assign({}, DefaultStructureProps, _props, props)
  95. unitsVisuals.forEach(async ({ visual, group }) => {
  96. if (!await visual.update(ctx, _props)) {
  97. await visual.create(ctx, group, _props)
  98. }
  99. })
  100. if (structureVisual) {
  101. if (!await structureVisual.update(ctx, _props)) {
  102. await structureVisual.create(ctx, _structure, _props)
  103. }
  104. }
  105. })
  106. }
  107. function getLoci(pickingId: PickingId) {
  108. let loci: Loci = EmptyLoci
  109. unitsVisuals.forEach(({ visual }) => {
  110. const _loci = visual.getLoci(pickingId)
  111. if (!isEmptyLoci(_loci)) loci = _loci
  112. })
  113. return loci
  114. }
  115. function mark(loci: Loci, action: MarkerAction) {
  116. unitsVisuals.forEach(({ visual }) => visual.mark(loci, action))
  117. }
  118. function destroy() {
  119. unitsVisuals.forEach(({ visual }) => visual.destroy())
  120. unitsVisuals.clear()
  121. }
  122. return {
  123. get renderObjects() {
  124. const renderObjects: RenderObject[] = []
  125. unitsVisuals.forEach(({ visual }) => renderObjects.push(...visual.renderObjects))
  126. return renderObjects
  127. },
  128. create,
  129. update,
  130. getLoci,
  131. mark,
  132. destroy
  133. }
  134. }