assembly.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. /**
  2. * Copyright (c) 2017-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { Mat4, Tensor, Vec3 } from '../../../mol-math/linear-algebra';
  7. import { SymmetryOperator } from '../../../mol-math/geometry/symmetry-operator';
  8. import { Assembly, OperatorGroup, OperatorGroups } from '../../../mol-model/structure/model/properties/symmetry';
  9. import { Queries as Q } from '../../../mol-model/structure';
  10. import { StructureProperties } from '../../../mol-model/structure';
  11. import { Table } from '../../../mol-data/db';
  12. import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
  13. type StructAssembly = Table<mmCIF_Schema['pdbx_struct_assembly']>
  14. type StructAssemblyGen = Table<mmCIF_Schema['pdbx_struct_assembly_gen']>
  15. type StructOperList = Table<mmCIF_Schema['pdbx_struct_oper_list']>
  16. export function createAssemblies(pdbx_struct_assembly: StructAssembly, pdbx_struct_assembly_gen: StructAssemblyGen, pdbx_struct_oper_list: StructOperList): ReadonlyArray<Assembly> {
  17. if (!pdbx_struct_assembly._rowCount) return [];
  18. const matrices = getMatrices(pdbx_struct_oper_list);
  19. const assemblies: Assembly[] = [];
  20. for (let i = 0; i < pdbx_struct_assembly._rowCount; i++) {
  21. assemblies[assemblies.length] = createAssembly(pdbx_struct_assembly, pdbx_struct_assembly_gen, i, matrices);
  22. }
  23. return assemblies;
  24. }
  25. type Matrices = Map<string, Mat4>
  26. type Generator = { assemblyId: string, expression: string, asymIds: string[] }
  27. function createAssembly(pdbx_struct_assembly: StructAssembly, pdbx_struct_assembly_gen: StructAssemblyGen, index: number, matrices: Matrices): Assembly {
  28. const id = pdbx_struct_assembly.id.value(index);
  29. const details = pdbx_struct_assembly.details.value(index);
  30. const generators: Generator[] = [];
  31. const { assembly_id, oper_expression, asym_id_list } = pdbx_struct_assembly_gen;
  32. for (let i = 0, _i = pdbx_struct_assembly_gen._rowCount; i < _i; i++) {
  33. if (assembly_id.value(i) !== id) continue;
  34. generators[generators.length] = {
  35. assemblyId: id,
  36. expression: oper_expression.value(i),
  37. asymIds: asym_id_list.value(i)
  38. };
  39. }
  40. return Assembly.create(id, details, operatorGroupsProvider(generators, matrices));
  41. }
  42. function operatorGroupsProvider(generators: Generator[], matrices: Matrices): () => OperatorGroups {
  43. return () => {
  44. const groups: OperatorGroup[] = [];
  45. let operatorOffset = 0;
  46. for (let i = 0; i < generators.length; i++) {
  47. const gen = generators[i];
  48. const operatorList = parseOperatorList(gen.expression);
  49. const operatorNames = expandOperators(operatorList);
  50. const operators = getAssemblyOperators(matrices, operatorNames, operatorOffset, gen.assemblyId);
  51. const selector = Q.generators.atoms({ chainTest: Q.pred.and(
  52. Q.pred.eq(ctx => StructureProperties.unit.operator_name(ctx.element), SymmetryOperator.DefaultName),
  53. Q.pred.inSet(ctx => StructureProperties.chain.label_asym_id(ctx.element), gen.asymIds)
  54. ) });
  55. groups[groups.length] = { selector, operators, asymIds: gen.asymIds };
  56. operatorOffset += operators.length;
  57. }
  58. return groups;
  59. };
  60. }
  61. function getMatrices(pdbx_struct_oper_list: StructOperList): Matrices {
  62. const { id, matrix, vector, _schema } = pdbx_struct_oper_list;
  63. const matrices = new Map<string, Mat4>();
  64. const t = Vec3();
  65. for (let i = 0, _i = pdbx_struct_oper_list._rowCount; i < _i; i++) {
  66. const m = Tensor.toMat4(Mat4(), _schema.matrix.space, matrix.value(i));
  67. Tensor.toVec3(t, _schema.vector.space, vector.value(i));
  68. Mat4.setTranslation(m, t);
  69. Mat4.setValue(m, 3, 3, 1);
  70. matrices.set(id.value(i), m);
  71. }
  72. return matrices;
  73. }
  74. function expandOperators(operatorList: string[][]) {
  75. const ops: string[][] = [];
  76. const currentOp: string[] = [];
  77. for (let i = 0; i < operatorList.length; i++) currentOp[i] = '';
  78. expandOperators1(operatorList, ops, operatorList.length - 1, currentOp);
  79. return ops;
  80. }
  81. function expandOperators1(operatorNames: string[][], list: string[][], i: number, current: string[]) {
  82. if (i < 0) {
  83. list[list.length] = current.slice(0);
  84. return;
  85. }
  86. const ops = operatorNames[i], len = ops.length;
  87. for (let j = 0; j < len; j++) {
  88. current[i] = ops[j];
  89. expandOperators1(operatorNames, list, i - 1, current);
  90. }
  91. }
  92. function getAssemblyOperators(matrices: Matrices, operatorNames: string[][], startIndex: number, assemblyId: string) {
  93. const operators: SymmetryOperator[] = [];
  94. let index = startIndex;
  95. for (const op of operatorNames) {
  96. const m = Mat4.identity();
  97. for (let i = 0; i < op.length; i++) {
  98. Mat4.mul(m, m, matrices.get(op[i])!);
  99. }
  100. index++;
  101. operators[operators.length] = SymmetryOperator.create(`ASM_${index}`, m, { assembly: { id: assemblyId, operId: index, operList: op } });
  102. }
  103. return operators;
  104. }
  105. function parseOperatorList(value: string): string[][] {
  106. // '(X0)(1-5)' becomes [['X0'], ['1', '2', '3', '4', '5']]
  107. // kudos to Glen van Ginkel.
  108. const oeRegex = /\(?([^\(\)]+)\)?]*/g, groups: string[] = [], ret: string[][] = [];
  109. let g: any;
  110. while (g = oeRegex.exec(value)) groups[groups.length] = g[1];
  111. groups.forEach(g => {
  112. const group: string[] = [];
  113. g.split(',').forEach(e => {
  114. const dashIndex = e.indexOf('-');
  115. if (dashIndex > 0) {
  116. const from = parseInt(e.substring(0, dashIndex)), to = parseInt(e.substr(dashIndex + 1));
  117. for (let i = from; i <= to; i++) group[group.length] = i.toString();
  118. } else {
  119. group[group.length] = e.trim();
  120. }
  121. });
  122. ret[ret.length] = group;
  123. });
  124. return ret;
  125. }