custom-element-property.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /**
  2. * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { ElementIndex, Model, CustomPropertyDescriptor } from 'mol-model/structure';
  7. import { StructureElement } from 'mol-model/structure/structure';
  8. import { Location } from 'mol-model/location';
  9. import { CustomPropertyRegistry } from './custom-property-registry';
  10. import { Task } from 'mol-task';
  11. import { ThemeDataContext, ThemeProvider } from 'mol-theme/theme';
  12. import { ColorTheme, LocationColor } from 'mol-theme/color';
  13. import { Color } from 'mol-util/color';
  14. import { TableLegend } from 'mol-util/color/tables';
  15. import { Loci } from 'mol-model/loci';
  16. import { OrderedSet } from 'mol-data/int';
  17. export { CustomElementProperty };
  18. namespace CustomElementProperty {
  19. export interface CreateParams<T> {
  20. isStatic: boolean,
  21. name: string,
  22. autoAttach?: boolean,
  23. display: string,
  24. attachableTo?: (model: Model) => boolean,
  25. getData(model: Model): Map<ElementIndex, T> | Promise<Map<ElementIndex, T>>,
  26. format?(e: T): string | undefined,
  27. coloring?: {
  28. getColor: (e: T) => Color,
  29. defaultColor: Color
  30. }
  31. }
  32. export function create<T>(params: CreateParams<T>) {
  33. const name = params.name;
  34. const Descriptor = CustomPropertyDescriptor({
  35. isStatic: params.isStatic,
  36. name: params.name,
  37. });
  38. function attach(model: Model) {
  39. return Task.create(`Attach ${params.display}`, async () => {
  40. try {
  41. if (model.customProperties.has(Descriptor)) return true;
  42. const data = await params.getData(model);
  43. if (params.isStatic) {
  44. model._staticPropertyData[name] = data;
  45. } else {
  46. model._dynamicPropertyData[name] = data;
  47. }
  48. model.customProperties.add(Descriptor);
  49. return true;
  50. } catch (e) {
  51. console.warn('Attach Property', e);
  52. return false;
  53. }
  54. })
  55. }
  56. function getStatic(e: StructureElement) { return e.unit.model._staticPropertyData[name].get(e.element); }
  57. function getDynamic(e: StructureElement) { return e.unit.model._staticPropertyData[name].get(e.element); }
  58. const propertyProvider: CustomPropertyRegistry.Provider = {
  59. option: [name, params.display],
  60. descriptor: Descriptor,
  61. defaultSelected: !!params.autoAttach,
  62. attachableTo: params.attachableTo || (() => true),
  63. attach
  64. };
  65. const get = params.isStatic ? getStatic : getDynamic;
  66. function has(model: Model) { return model.customProperties.has(Descriptor); }
  67. function Coloring(ctx: ThemeDataContext, props: {}): ColorTheme<{}> {
  68. let color: LocationColor;
  69. const getColor = params.coloring!.getColor;
  70. const defaultColor = params.coloring!.defaultColor;
  71. if (ctx.structure && !ctx.structure.isEmpty && has(ctx.structure.models[0])) {
  72. color = (location: Location) => {
  73. if (StructureElement.isLocation(location)) {
  74. const e = get(location);
  75. if (typeof e !== 'undefined') return getColor(e);
  76. }
  77. return defaultColor;
  78. }
  79. } else {
  80. color = () => defaultColor;
  81. }
  82. return {
  83. factory: Coloring,
  84. granularity: 'group',
  85. color: color,
  86. props: props,
  87. description: 'Assign element colors based on the provided data.',
  88. legend: TableLegend([])
  89. };
  90. }
  91. const colorTheme: ThemeProvider<ColorTheme<{}>, {}> = {
  92. label: params.display,
  93. factory: Coloring,
  94. getParams: () => ({}),
  95. defaultValues: {},
  96. isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && !ctx.structure.isEmpty && has(ctx.structure.models[0])
  97. }
  98. function LabelProvider(loci: Loci): string | undefined {
  99. if (loci.kind === 'element-loci') {
  100. const e = loci.elements[0];
  101. if (!has(e.unit.model)) return void 0;
  102. return params.format!(get(StructureElement.create(e.unit, e.unit.elements[OrderedSet.getAt(e.indices, 0)])));
  103. }
  104. return void 0;
  105. }
  106. return {
  107. Descriptor,
  108. attach,
  109. get,
  110. propertyProvider,
  111. colorTheme: params.coloring ? colorTheme : void 0,
  112. labelProvider: params.format ? LabelProvider : ((loci: Loci) => void 0)
  113. };
  114. }
  115. }