root-structure.ts 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /**
  2. * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  6. */
  7. import { Model, Structure, StructureSymmetry } from '../../mol-model/structure';
  8. import { stringToWords } from '../../mol-util/string';
  9. import { SpacegroupCell, Spacegroup } from '../../mol-math/geometry';
  10. import { ParamDefinition as PD } from '../../mol-util/param-definition';
  11. import { Vec3 } from '../../mol-math/linear-algebra';
  12. import { RuntimeContext } from '../../mol-task';
  13. import { PluginContext } from '../../mol-plugin/context';
  14. import { Assembly, Symmetry } from '../../mol-model/structure/model/properties/symmetry';
  15. import { PluginStateObject as SO } from '../objects';
  16. import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
  17. export namespace RootStructureDefinition {
  18. export function getParams(model?: Model, defaultValue?: 'auto' | 'model' | 'assembly' | 'symmetry' | 'symmetry-mates' | 'symmetry-assembly') {
  19. const symmetry = model && ModelSymmetry.Provider.get(model);
  20. const assemblyIds = symmetry ? symmetry.assemblies.map(a => [a.id, `${a.id}: ${stringToWords(a.details)}`] as [string, string]) : [];
  21. const showSymm = !symmetry ? true : !SpacegroupCell.isZero(symmetry.spacegroup.cell);
  22. const operatorOptions: [number, string][] = [];
  23. if (symmetry) {
  24. const { operators } = symmetry.spacegroup;
  25. for (let i = 0, il = operators.length; i < il; i++) {
  26. operatorOptions.push([i, `${i + 1}: ${Spacegroup.getOperatorXyz(operators[i])}`]);
  27. }
  28. }
  29. const asymIdsOptions: [string, string][] = [];
  30. if (model) {
  31. model.properties.structAsymMap.forEach(v => {
  32. const label = v.id === v.auth_id ? v.id : `${v.id} [auth ${v.auth_id}]`;
  33. asymIdsOptions.push([v.id, label]);
  34. });
  35. }
  36. const modes = {
  37. auto: PD.EmptyGroup(),
  38. model: PD.EmptyGroup(),
  39. assembly: PD.Group({
  40. id: PD.Optional(model
  41. ? PD.Select(assemblyIds.length ? assemblyIds[0][0] : '', assemblyIds, { label: 'Asm Id', description: 'Assembly Id' })
  42. : PD.Text('', { label: 'Asm Id', description: 'Assembly Id (use empty for the 1st assembly)' }))
  43. }, { isFlat: true }),
  44. 'symmetry-mates': PD.Group({
  45. radius: PD.Numeric(5, { min: 0, max: 50, step: 1 })
  46. }, { isFlat: true }),
  47. 'symmetry': PD.Group({
  48. ijkMin: PD.Vec3(Vec3.create(-1, -1, -1), { step: 1 }, { label: 'Min IJK', fieldLabels: { x: 'I', y: 'J', z: 'K' } }),
  49. ijkMax: PD.Vec3(Vec3.create(1, 1, 1), { step: 1 }, { label: 'Max IJK', fieldLabels: { x: 'I', y: 'J', z: 'K' } })
  50. }, { isFlat: true }),
  51. 'symmetry-assembly': PD.Group({
  52. generators: PD.ObjectList({
  53. operators: PD.ObjectList({
  54. index: PD.Select(0, operatorOptions),
  55. shift: PD.Vec3(Vec3(), { step: 1 }, { label: 'IJK', fieldLabels: { x: 'I', y: 'J', z: 'K' } })
  56. }, e => `${e.index + 1}_${e.shift.map(a => a + 5).join('')}`, {
  57. defaultValue: [] as { index: number, shift: Vec3 }[]
  58. }),
  59. asymIds: PD.MultiSelect([] as string[], asymIdsOptions)
  60. }, e => `${e.asymIds.length} asym ids, ${e.operators.length} operators`, {
  61. defaultValue: [] as { operators: { index: number, shift: Vec3 }[], asymIds: string[] }[]
  62. })
  63. }, { isFlat: true })
  64. };
  65. const options: [keyof typeof modes, string][] = [];
  66. if (defaultValue === 'auto') {
  67. options.push(['auto', 'Auto']);
  68. }
  69. options.push(['model', 'Model']);
  70. if (assemblyIds.length > 0) {
  71. options.push(['assembly', 'Assembly']);
  72. }
  73. if (showSymm) {
  74. options.push(['symmetry-mates', 'Symmetry Mates']);
  75. options.push(['symmetry', 'Symmetry (indices)']);
  76. options.push(['symmetry-assembly', 'Symmetry (assembly)']);
  77. }
  78. return {
  79. type: PD.MappedStatic(defaultValue || 'model', modes, { options })
  80. };
  81. }
  82. export type Params = PD.Values<ReturnType<typeof getParams>>['type']
  83. export function canAutoUpdate(oldParams: Params, newParams: Params) {
  84. if (newParams.name === 'symmetry-assembly' || (newParams.name === 'symmetry' && oldParams.name === 'symmetry')) return false;
  85. return true;
  86. }
  87. async function buildAssembly(plugin: PluginContext, ctx: RuntimeContext, model: Model, id?: string) {
  88. let asm: Assembly | undefined = void 0;
  89. const symmetry = ModelSymmetry.Provider.get(model);
  90. // if no id is specified, use the 1st assembly.
  91. if (!id && symmetry && symmetry.assemblies.length !== 0) {
  92. id = symmetry.assemblies[0].id;
  93. }
  94. if (!symmetry || symmetry.assemblies.length === 0) {
  95. plugin.log.warn(`Model '${model.entryId}' has no assembly, returning model structure.`);
  96. } else {
  97. asm = Symmetry.findAssembly(model, id || '');
  98. if (!asm) {
  99. plugin.log.warn(`Model '${model.entryId}' has no assembly called '${id}', returning model structure.`);
  100. }
  101. }
  102. const base = Structure.ofModel(model);
  103. if (!asm) {
  104. const label = { label: 'Model', description: Structure.elementDescription(base) };
  105. return new SO.Molecule.Structure(base, label);
  106. }
  107. id = asm.id;
  108. const s = await StructureSymmetry.buildAssembly(base, id!).runInContext(ctx);
  109. const props = { label: `Assembly ${id}`, description: Structure.elementDescription(s) };
  110. return new SO.Molecule.Structure(s, props);
  111. }
  112. async function buildSymmetry(ctx: RuntimeContext, model: Model, ijkMin: Vec3, ijkMax: Vec3) {
  113. const base = Structure.ofModel(model);
  114. const s = await StructureSymmetry.buildSymmetryRange(base, ijkMin, ijkMax).runInContext(ctx);
  115. const props = { label: `Symmetry [${ijkMin}] to [${ijkMax}]`, description: Structure.elementDescription(s) };
  116. return new SO.Molecule.Structure(s, props);
  117. }
  118. async function buildSymmetryMates(ctx: RuntimeContext, model: Model, radius: number) {
  119. const base = Structure.ofModel(model);
  120. const s = await StructureSymmetry.builderSymmetryMates(base, radius).runInContext(ctx);
  121. const props = { label: `Symmetry Mates`, description: Structure.elementDescription(s) };
  122. return new SO.Molecule.Structure(s, props);
  123. }
  124. async function buildSymmetryAssembly(ctx: RuntimeContext, model: Model, generators: StructureSymmetry.Generators, symmetry: Symmetry) {
  125. const base = Structure.ofModel(model);
  126. const s = await StructureSymmetry.buildSymmetryAssembly(base, generators, symmetry).runInContext(ctx);
  127. const props = { label: `Symmetry Assembly`, description: Structure.elementDescription(s) };
  128. return new SO.Molecule.Structure(s, props);
  129. }
  130. export async function create(plugin: PluginContext, ctx: RuntimeContext, model: Model, params?: Params): Promise<SO.Molecule.Structure> {
  131. const symmetry = ModelSymmetry.Provider.get(model);
  132. if (!symmetry || !params || params.name === 'model') {
  133. const s = Structure.ofModel(model);
  134. return new SO.Molecule.Structure(s, { label: 'Model', description: Structure.elementDescription(s) });
  135. }
  136. if (params.name === 'auto') {
  137. if (symmetry.assemblies.length === 0) {
  138. const s = Structure.ofModel(model);
  139. return new SO.Molecule.Structure(s, { label: 'Model', description: Structure.elementDescription(s) });
  140. } else {
  141. return buildAssembly(plugin, ctx, model);
  142. }
  143. }
  144. if (params.name === 'assembly') {
  145. return buildAssembly(plugin, ctx, model, params.params.id);
  146. }
  147. if (params.name === 'symmetry') {
  148. return buildSymmetry(ctx, model, params.params.ijkMin, params.params.ijkMax);
  149. }
  150. if (params.name === 'symmetry-mates') {
  151. return buildSymmetryMates(ctx, model, params.params.radius);
  152. }
  153. if (params.name === 'symmetry-assembly') {
  154. return buildSymmetryAssembly(ctx, model, params.params.generators, symmetry);
  155. }
  156. throw new Error(`Unknown represetation type: ${(params as any).name}`);
  157. }
  158. }