representation.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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. */
  6. import { Task, RuntimeContext } from 'mol-task'
  7. import { RenderObject } from 'mol-gl/render-object'
  8. import { PickingId } from '../mol-geo/geometry/picking';
  9. import { Loci, isEmptyLoci, EmptyLoci } from 'mol-model/loci';
  10. import { MarkerAction } from '../mol-geo/geometry/marker-data';
  11. import { ParamDefinition as PD } from 'mol-util/param-definition';
  12. import { WebGLContext } from 'mol-gl/webgl/context';
  13. import { getQualityProps } from './util';
  14. import { ColorTheme } from 'mol-theme/color';
  15. import { SizeTheme } from 'mol-theme/size';
  16. import { Theme, ThemeRegistryContext } from 'mol-theme/theme';
  17. import { Subject } from 'rxjs';
  18. // export interface RepresentationProps {
  19. // visuals?: string[]
  20. // }
  21. export type RepresentationProps = { [k: string]: any }
  22. export type RepresentationParamsGetter<D, P extends PD.Params> = (ctx: ThemeRegistryContext, data: D) => P
  23. //
  24. export interface RepresentationProvider<D, P extends PD.Params> {
  25. readonly label: string
  26. readonly description: string
  27. readonly factory: (getParams: RepresentationParamsGetter<D, P>) => Representation<D, P>
  28. readonly getParams: (ctx: ThemeRegistryContext, data: D) => P
  29. readonly defaultValues: PD.Values<P>
  30. }
  31. export type AnyRepresentationProvider = RepresentationProvider<any, {}>
  32. export const EmptyRepresentationProvider = {
  33. label: '',
  34. description: '',
  35. factory: () => Representation.Empty,
  36. getParams: () => ({}),
  37. defaultValues: {}
  38. }
  39. export class RepresentationRegistry<D> {
  40. private _list: { name: string, provider: RepresentationProvider<D, any> }[] = []
  41. private _map = new Map<string, RepresentationProvider<D, any>>()
  42. get default() { return this._list[0]; }
  43. get types(): [string, string][] {
  44. return this._list.map(e => [e.name, e.provider.label] as [string, string]);
  45. }
  46. constructor() {};
  47. add<P extends PD.Params>(name: string, provider: RepresentationProvider<D, P>) {
  48. this._list.push({ name, provider })
  49. this._map.set(name, provider)
  50. }
  51. get<P extends PD.Params>(name: string): RepresentationProvider<D, P> {
  52. return this._map.get(name) || EmptyRepresentationProvider as unknown as RepresentationProvider<D, P>
  53. }
  54. get list() {
  55. return this._list
  56. }
  57. }
  58. //
  59. export interface RepresentationContext {
  60. webgl?: WebGLContext
  61. colorThemeRegistry: ColorTheme.Registry
  62. sizeThemeRegistry: SizeTheme.Registry
  63. }
  64. export { Representation }
  65. interface Representation<D, P extends PD.Params = {}> {
  66. readonly label: string
  67. readonly updated: Subject<number>
  68. readonly renderObjects: ReadonlyArray<RenderObject>
  69. readonly props: Readonly<PD.Values<P>>
  70. readonly params: Readonly<P>
  71. createOrUpdate: (ctx: RepresentationContext, props?: Partial<PD.Values<P>>, data?: D) => Task<void>
  72. getLoci: (pickingId: PickingId) => Loci
  73. mark: (loci: Loci, action: MarkerAction) => boolean
  74. setVisibility: (value: boolean) => void
  75. setPickable: (value: boolean) => void
  76. destroy: () => void
  77. }
  78. namespace Representation {
  79. export type Any = Representation<any>
  80. export const Empty: Any = {
  81. label: '', renderObjects: [], props: {}, params: {}, updated: new Subject(),
  82. createOrUpdate: () => Task.constant('', undefined),
  83. getLoci: () => EmptyLoci,
  84. mark: () => false,
  85. setVisibility: () => {},
  86. setPickable: () => {},
  87. destroy: () => {}
  88. }
  89. export type Def<D, P extends PD.Params = {}> = { [k: string]: (getParams: RepresentationParamsGetter<D, P>) => Representation<any, P> }
  90. export function createMulti<D, P extends PD.Params = {}>(label: string, getParams: RepresentationParamsGetter<D, P>, reprDefs: Def<D, P>): Representation<D, P> {
  91. let version = 0
  92. const updated = new Subject<number>()
  93. let currentParams: P
  94. let currentProps: PD.Values<P>
  95. let currentData: D
  96. const reprMap: { [k: number]: string } = {}
  97. const reprList: Representation<D, P>[] = Object.keys(reprDefs).map((name, i) => {
  98. reprMap[i] = name
  99. return reprDefs[name](getParams)
  100. })
  101. return {
  102. label,
  103. updated,
  104. get renderObjects() {
  105. const renderObjects: RenderObject[] = []
  106. if (currentProps) {
  107. const { visuals } = currentProps
  108. for (let i = 0, il = reprList.length; i < il; ++i) {
  109. if (!visuals || visuals.includes(reprMap[i])) {
  110. renderObjects.push(...reprList[i].renderObjects)
  111. }
  112. }
  113. }
  114. return renderObjects
  115. },
  116. get props() {
  117. const props = {}
  118. reprList.forEach(r => Object.assign(props, r.props))
  119. return props as P
  120. },
  121. get params() { return currentParams },
  122. createOrUpdate: (ctx: RepresentationContext, props: Partial<P> = {}, data?: D) => {
  123. if (data && data !== currentData) {
  124. currentParams = getParams(ctx, data)
  125. currentData = data
  126. if (!currentProps) currentProps = PD.getDefaultValues(currentParams) as P
  127. }
  128. const qualityProps = getQualityProps(Object.assign({}, currentProps, props), currentData)
  129. Object.assign(currentProps, props, qualityProps)
  130. const { visuals } = currentProps
  131. return Task.create(`Creating '${label}' representation`, async runtime => {
  132. for (let i = 0, il = reprList.length; i < il; ++i) {
  133. if (!visuals || visuals.includes(reprMap[i])) {
  134. await reprList[i].createOrUpdate(ctx, currentProps, currentData).runInContext(runtime)
  135. }
  136. }
  137. updated.next(version++)
  138. })
  139. },
  140. getLoci: (pickingId: PickingId) => {
  141. for (let i = 0, il = reprList.length; i < il; ++i) {
  142. const loci = reprList[i].getLoci(pickingId)
  143. if (!isEmptyLoci(loci)) return loci
  144. }
  145. return EmptyLoci
  146. },
  147. mark: (loci: Loci, action: MarkerAction) => {
  148. let marked = false
  149. for (let i = 0, il = reprList.length; i < il; ++i) {
  150. marked = reprList[i].mark(loci, action) || marked
  151. }
  152. return marked
  153. },
  154. setVisibility: (value: boolean) => {
  155. for (let i = 0, il = reprList.length; i < il; ++i) {
  156. reprList[i].setVisibility(value)
  157. }
  158. },
  159. setPickable: (value: boolean) => {
  160. for (let i = 0, il = reprList.length; i < il; ++i) {
  161. reprList[i].setPickable(value)
  162. }
  163. },
  164. destroy() {
  165. for (let i = 0, il = reprList.length; i < il; ++i) {
  166. reprList[i].destroy()
  167. }
  168. }
  169. }
  170. }
  171. }
  172. //
  173. export interface VisualContext {
  174. webgl?: WebGLContext
  175. runtime: RuntimeContext,
  176. }
  177. export interface Visual<D, P extends PD.Params> {
  178. readonly renderObject: RenderObject | undefined
  179. createOrUpdate: (ctx: VisualContext, theme: Theme, props?: Partial<PD.Values<P>>, data?: D) => Promise<void>
  180. getLoci: (pickingId: PickingId) => Loci
  181. mark: (loci: Loci, action: MarkerAction) => boolean
  182. setVisibility: (value: boolean) => void
  183. setPickable: (value: boolean) => void
  184. destroy: () => void
  185. }