units-representation.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  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, 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 { PickingId } from '../../geometry/picking';
  12. import { Loci, EmptyLoci, isEmptyLoci } from 'mol-model/loci';
  13. import { MarkerAction } from '../../geometry/marker-data';
  14. import { StructureProps } from '.';
  15. import { StructureGroup } from './units-visual';
  16. export interface UnitsVisual<P extends RepresentationProps = {}> extends Visual<StructureGroup, P> { }
  17. export interface StructureVisual<P extends RepresentationProps = {}> extends Visual<Structure, P> { }
  18. export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P> { }
  19. export function UnitsRepresentation<P extends StructureProps>(label: string, visualCtor: () => UnitsVisual<P>): StructureRepresentation<P> {
  20. let visuals = new Map<number, { group: Unit.SymmetryGroup, visual: UnitsVisual<P> }>()
  21. let _props: P
  22. let _structure: Structure
  23. let _groups: ReadonlyArray<Unit.SymmetryGroup>
  24. function createOrUpdate(props: Partial<P> = {}, structure?: Structure) {
  25. _props = Object.assign({}, _props, props)
  26. return Task.create('Creating or updating UnitsRepresentation', async ctx => {
  27. if (!_structure && !structure) {
  28. throw new Error('missing structure')
  29. } else if (structure && !_structure) {
  30. // console.log('initial structure')
  31. // First call with a structure, create visuals for each group.
  32. _groups = structure.unitSymmetryGroups;
  33. for (let i = 0; i < _groups.length; i++) {
  34. const group = _groups[i];
  35. const visual = visualCtor()
  36. await visual.createOrUpdate(ctx, _props, { group, structure })
  37. visuals.set(group.hashCode, { visual, group })
  38. }
  39. } else if (structure && _structure.hashCode !== structure.hashCode) {
  40. // console.log('_structure.hashCode !== structure.hashCode')
  41. // Tries to re-use existing visuals for the groups of the new structure.
  42. // Creates additional visuals if needed, destroys left-over visuals.
  43. _groups = structure.unitSymmetryGroups;
  44. // const newGroups: Unit.SymmetryGroup[] = []
  45. const oldVisuals = visuals
  46. visuals = new Map()
  47. for (let i = 0; i < _groups.length; i++) {
  48. const group = _groups[i];
  49. const visualGroup = oldVisuals.get(group.hashCode)
  50. if (visualGroup) {
  51. const { visual } = visualGroup
  52. await visual.createOrUpdate(ctx, _props, { group, structure })
  53. visuals.set(group.hashCode, { visual, group })
  54. oldVisuals.delete(group.hashCode)
  55. } else {
  56. // newGroups.push(group)
  57. const visual = visualCtor()
  58. await visual.createOrUpdate(ctx, _props, { group, structure })
  59. visuals.set(group.hashCode, { visual, group })
  60. }
  61. }
  62. oldVisuals.forEach(({ visual }) => visual.destroy())
  63. // TODO review logic
  64. // For new groups, re-use left-over visuals
  65. // const unusedVisuals: UnitsVisual<P>[] = []
  66. // oldVisuals.forEach(({ visual }) => unusedVisuals.push(visual))
  67. // newGroups.forEach(async group => {
  68. // const visual = unusedVisuals.pop() || visualCtor()
  69. // await visual.createOrUpdate(ctx, _props, group)
  70. // visuals.set(group.hashCode, { visual, group })
  71. // })
  72. // unusedVisuals.forEach(visual => visual.destroy())
  73. } else if (structure && _structure.hashCode === structure.hashCode) {
  74. // console.log('_structure.hashCode === structure.hashCode')
  75. // Expects that for structures with the same hashCode,
  76. // the unitSymmetryGroups are the same as well.
  77. // Re-uses existing visuals for the groups of the new structure.
  78. _groups = structure.unitSymmetryGroups;
  79. for (let i = 0; i < _groups.length; i++) {
  80. const group = _groups[i];
  81. const visualGroup = visuals.get(group.hashCode)
  82. if (visualGroup) {
  83. await visualGroup.visual.createOrUpdate(ctx, _props, { group, structure })
  84. visualGroup.group = group
  85. } else {
  86. throw new Error(`expected to find visual for hashCode ${group.hashCode}`)
  87. }
  88. }
  89. } else {
  90. // console.log('no new structure')
  91. // No new structure given, just update all visuals with new props.
  92. const visualsList: [ UnitsVisual<P>, Unit.SymmetryGroup ][] = [] // TODO avoid allocation
  93. visuals.forEach(({ visual, group }) => visualsList.push([ visual, group ]))
  94. for (let i = 0, il = visualsList.length; i < il; ++i) {
  95. const [ visual, group ] = visualsList[i]
  96. await visual.createOrUpdate(ctx, _props, { group, structure: _structure })
  97. }
  98. }
  99. if (structure) _structure = structure
  100. });
  101. }
  102. function getLoci(pickingId: PickingId) {
  103. let loci: Loci = EmptyLoci
  104. visuals.forEach(({ visual }) => {
  105. const _loci = visual.getLoci(pickingId)
  106. if (!isEmptyLoci(_loci)) loci = _loci
  107. })
  108. return loci
  109. }
  110. function mark(loci: Loci, action: MarkerAction) {
  111. let changed = false
  112. visuals.forEach(({ visual }) => {
  113. changed = visual.mark(loci, action) || changed
  114. })
  115. return changed
  116. }
  117. function destroy() {
  118. visuals.forEach(({ visual }) => visual.destroy())
  119. visuals.clear()
  120. }
  121. return {
  122. label,
  123. get renderObjects() {
  124. const renderObjects: RenderObject[] = []
  125. visuals.forEach(({ visual }) => {
  126. if (visual.renderObject) renderObjects.push(visual.renderObject)
  127. })
  128. return renderObjects
  129. },
  130. get props() {
  131. return _props
  132. },
  133. createOrUpdate,
  134. getLoci,
  135. mark,
  136. destroy
  137. }
  138. }