operator-hkl.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. /**
  2. * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { Color } from '../../mol-util/color';
  7. import { StructureElement, Bond, Structure } from '../../mol-model/structure';
  8. import { Location } from '../../mol-model/location';
  9. import { ColorTheme, LocationColor } from '../color';
  10. import { ParamDefinition as PD } from '../../mol-util/param-definition';
  11. import { ThemeDataContext } from '../theme';
  12. import { getPaletteParams, getPalette } from '../../mol-util/color/palette';
  13. import { ScaleLegend, TableLegend } from '../../mol-util/legend';
  14. import { Vec3 } from '../../mol-math/linear-algebra';
  15. import { integerDigitCount } from '../../mol-util/number';
  16. import { ColorLists, getColorListFromName } from '../../mol-util/color/lists';
  17. const DefaultList = 'dark-2';
  18. const DefaultColor = Color(0xCCCCCC);
  19. const Description = `Assigns a color based on the operator HKL value of a transformed chain.`;
  20. export const OperatorHklColorThemeParams = {
  21. ...getPaletteParams({ type: 'colors', colorList: DefaultList }),
  22. };
  23. export type OperatorHklColorThemeParams = typeof OperatorHklColorThemeParams
  24. export function getOperatorHklColorThemeParams(ctx: ThemeDataContext) {
  25. const params = PD.clone(OperatorHklColorThemeParams);
  26. if (ctx.structure) {
  27. if (getOperatorHklSerialMap(ctx.structure.root).map.size > ColorLists[DefaultList].list.length) {
  28. params.palette.defaultValue.name = 'colors';
  29. params.palette.defaultValue.params = {
  30. ...params.palette.defaultValue.params,
  31. list: { kind: 'interpolate', colors: getColorListFromName(DefaultList).list }
  32. };
  33. }
  34. }
  35. return params;
  36. }
  37. const hklOffset = 10000;
  38. function hklKey(hkl: Vec3) {
  39. return hkl.map(v => `${v + hklOffset}`.padStart(5, '0')).join('');
  40. }
  41. function hklKeySplit(key: string) {
  42. const len = integerDigitCount(hklOffset, 0);
  43. const h = parseInt(key.substr(0, len));
  44. const k = parseInt(key.substr(len, len));
  45. const l = parseInt(key.substr(len + len, len));
  46. return [ h - hklOffset, k - hklOffset, l - hklOffset ] as Vec3;
  47. }
  48. function formatHkl(hkl: Vec3) {
  49. return hkl.map(v => v + 5).join('');
  50. }
  51. function getOperatorHklSerialMap(structure: Structure) {
  52. const map = new Map<string, number>();
  53. const set = new Set<string>();
  54. for (let i = 0, il = structure.units.length; i < il; ++i) {
  55. const k = hklKey(structure.units[i].conformation.operator.hkl);
  56. set.add(k);
  57. }
  58. const arr = Array.from(set.values()).sort();
  59. arr.forEach(k => map.set(k, map.size));
  60. const min = hklKeySplit(arr[0]);
  61. const max = hklKeySplit(arr[arr.length - 1]);
  62. return { min, max, map };
  63. }
  64. export function OperatorHklColorTheme(ctx: ThemeDataContext, props: PD.Values<OperatorHklColorThemeParams>): ColorTheme<OperatorHklColorThemeParams> {
  65. let color: LocationColor;
  66. let legend: ScaleLegend | TableLegend | undefined;
  67. if (ctx.structure) {
  68. const { min, max, map } = getOperatorHklSerialMap(ctx.structure.root);
  69. const labelTable: string[] = [];
  70. map.forEach((v, k) => {
  71. const i = v % map.size;
  72. const label = formatHkl(hklKeySplit(k));
  73. if (labelTable[i] === undefined) labelTable[i] = label;
  74. else labelTable[i] += `, ${label}`;
  75. });
  76. const labelOptions = {
  77. minLabel: formatHkl(min),
  78. maxLabel: formatHkl(max),
  79. valueLabel: (i: number) => labelTable[i]
  80. };
  81. const palette = getPalette(map.size, props, labelOptions);
  82. legend = palette.legend;
  83. color = (location: Location): Color => {
  84. let serial: number | undefined = undefined;
  85. if (StructureElement.Location.is(location)) {
  86. const k = hklKey(location.unit.conformation.operator.hkl);
  87. serial = map.get(k);
  88. } else if (Bond.isLocation(location)) {
  89. const k = hklKey(location.aUnit.conformation.operator.hkl);
  90. serial = map.get(k);
  91. }
  92. return serial === undefined ? DefaultColor : palette.color(serial);
  93. };
  94. } else {
  95. color = () => DefaultColor;
  96. }
  97. return {
  98. factory: OperatorHklColorTheme,
  99. granularity: 'instance',
  100. color,
  101. props,
  102. description: Description,
  103. legend
  104. };
  105. }
  106. export const OperatorHklColorThemeProvider: ColorTheme.Provider<OperatorHklColorThemeParams, 'operator-hkl'> = {
  107. name: 'operator-hkl',
  108. label: 'Operator HKL',
  109. category: ColorTheme.Category.Symmetry,
  110. factory: OperatorHklColorTheme,
  111. getParams: getOperatorHklColorThemeParams,
  112. defaultValues: PD.getDefaultValues(OperatorHklColorThemeParams),
  113. isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
  114. };