generators.ts 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /**
  2. * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { StructureQuery } from '../query'
  7. import { StructureSelection } from '../selection'
  8. import { Unit, StructureProperties as P } from '../../structure'
  9. import { Segmentation, SortedArray } from 'mol-data/int'
  10. import { LinearGroupingBuilder } from '../utils/builders';
  11. import { QueryPredicate, QueryFn, QueryContextView } from '../context';
  12. import { UnitRing } from '../../structure/unit/rings';
  13. import Structure from '../../structure/structure';
  14. import { ElementIndex } from '../../model';
  15. import { UniqueArray } from 'mol-data/generic';
  16. import { structureSubtract } from '../utils/structure-set';
  17. export const none: StructureQuery = ctx => StructureSelection.Sequence(ctx.inputStructure, []);
  18. export const all: StructureQuery = ctx => StructureSelection.Singletons(ctx.inputStructure, ctx.inputStructure);
  19. export interface AtomsQueryParams {
  20. /** Query to be executed for each entity once */
  21. entityTest: QueryPredicate,
  22. /** Query to be executed for each chain once */
  23. chainTest: QueryPredicate,
  24. /** Query to be executed for each residue once */
  25. residueTest: QueryPredicate,
  26. /** Query to be executed for each atom */
  27. atomTest: QueryPredicate,
  28. groupBy: QueryFn
  29. }
  30. export function residues(params?: Partial<AtomsQueryParams>) { return atoms({ ...params, groupBy: ctx => P.residue.key(ctx.element) }); }
  31. export function chains(params?: Partial<AtomsQueryParams>) { return atoms({ ...params, groupBy: ctx => P.chain.key(ctx.element) }); }
  32. function _true(ctx: QueryContextView) { return true; }
  33. function _zero(ctx: QueryContextView) { return 0; }
  34. export function atoms(params?: Partial<AtomsQueryParams>): StructureQuery {
  35. if (!params || (!params.atomTest && !params.residueTest && !params.chainTest && !params.entityTest && !params.groupBy)) return all;
  36. if (!!params.atomTest && !params.residueTest && !params.chainTest && !params.entityTest && !params.groupBy) return atomGroupsLinear(params.atomTest);
  37. const normalized: AtomsQueryParams = {
  38. entityTest: params.entityTest || _true,
  39. chainTest: params.chainTest || _true,
  40. residueTest: params.residueTest || _true,
  41. atomTest: params.atomTest || _true,
  42. groupBy: params.groupBy || _zero,
  43. };
  44. if (!params.groupBy) return atomGroupsSegmented(normalized)
  45. return atomGroupsGrouped(normalized);
  46. }
  47. function atomGroupsLinear(atomTest: QueryPredicate): StructureQuery {
  48. return ctx => {
  49. const { inputStructure } = ctx;
  50. const { units } = inputStructure;
  51. const l = ctx.pushCurrentElement();
  52. const builder = inputStructure.subsetBuilder(true);
  53. for (const unit of units) {
  54. l.unit = unit;
  55. const elements = unit.elements;
  56. builder.beginUnit(unit.id);
  57. for (let j = 0, _j = elements.length; j < _j; j++) {
  58. l.element = elements[j];
  59. if (atomTest(ctx)) builder.addElement(l.element);
  60. }
  61. builder.commitUnit();
  62. ctx.throwIfTimedOut();
  63. }
  64. ctx.popCurrentElement();
  65. return StructureSelection.Singletons(inputStructure, builder.getStructure());
  66. };
  67. }
  68. function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: AtomsQueryParams): StructureQuery {
  69. return ctx => {
  70. const { inputStructure } = ctx;
  71. const { units } = inputStructure;
  72. const l = ctx.pushCurrentElement();
  73. const builder = inputStructure.subsetBuilder(true);
  74. for (const unit of units) {
  75. if (unit.kind !== Unit.Kind.Atomic) continue;
  76. l.unit = unit;
  77. const elements = unit.elements;
  78. builder.beginUnit(unit.id);
  79. const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, elements);
  80. const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, elements);
  81. while (chainsIt.hasNext) {
  82. const chainSegment = chainsIt.move();
  83. l.element = elements[chainSegment.start];
  84. // test entity and chain
  85. if (!entityTest(ctx) || !chainTest(ctx)) continue;
  86. residuesIt.setSegment(chainSegment);
  87. while (residuesIt.hasNext) {
  88. const residueSegment = residuesIt.move();
  89. l.element = elements[residueSegment.start];
  90. // test residue
  91. if (!residueTest(ctx)) continue;
  92. for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) {
  93. l.element = elements[j];
  94. if (atomTest(ctx)) {
  95. builder.addElement(l.element);
  96. }
  97. }
  98. }
  99. }
  100. builder.commitUnit();
  101. ctx.throwIfTimedOut();
  102. }
  103. ctx.popCurrentElement();
  104. return StructureSelection.Singletons(inputStructure, builder.getStructure());
  105. };
  106. }
  107. function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, groupBy }: AtomsQueryParams): StructureQuery {
  108. return ctx => {
  109. const { inputStructure } = ctx;
  110. const { units } = inputStructure;
  111. const l = ctx.pushCurrentElement();
  112. const builder = new LinearGroupingBuilder(inputStructure);
  113. for (const unit of units) {
  114. if (unit.kind !== Unit.Kind.Atomic) continue;
  115. l.unit = unit;
  116. const elements = unit.elements;
  117. const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, elements);
  118. const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, elements);
  119. while (chainsIt.hasNext) {
  120. const chainSegment = chainsIt.move();
  121. l.element = elements[chainSegment.start];
  122. // test entity and chain
  123. if (!entityTest(ctx) || !chainTest(ctx)) continue;
  124. residuesIt.setSegment(chainSegment);
  125. while (residuesIt.hasNext) {
  126. const residueSegment = residuesIt.move();
  127. l.element = elements[residueSegment.start];
  128. // test residue
  129. if (!residueTest(ctx)) continue;
  130. for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) {
  131. l.element = elements[j];
  132. if (atomTest(ctx)) builder.add(groupBy(ctx), unit.id, l.element);
  133. }
  134. }
  135. }
  136. ctx.throwIfTimedOut();
  137. }
  138. ctx.popCurrentElement();
  139. return builder.getSelection();
  140. };
  141. }
  142. function getRingStructure(unit: Unit.Atomic, ring: UnitRing, inputStructure: Structure) {
  143. const elements = new Int32Array(ring.length) as any as ElementIndex[];
  144. for (let i = 0, _i = ring.length; i < _i; i++) elements[i] = unit.elements[ring[i]];
  145. return Structure.create([unit.getChild(SortedArray.ofSortedArray(elements))], inputStructure);
  146. }
  147. export function rings(fingerprints?: ArrayLike<UnitRing.Fingerprint>): StructureQuery {
  148. return ctx => {
  149. const { units } = ctx.inputStructure;
  150. let ret = StructureSelection.LinearBuilder(ctx.inputStructure);
  151. if (!fingerprints || fingerprints.length === 0) {
  152. for (const u of units) {
  153. if (!Unit.isAtomic(u)) continue;
  154. for (const r of u.rings.all) {
  155. ret.add(getRingStructure(u, r, ctx.inputStructure));
  156. }
  157. }
  158. } else {
  159. const uniqueFps = UniqueArray.create<UnitRing.Fingerprint, UnitRing.Fingerprint>();
  160. for (let i = 0; i < fingerprints.length; i++) UniqueArray.add(uniqueFps, fingerprints[i], fingerprints[i]);
  161. for (const u of units) {
  162. if (!Unit.isAtomic(u)) continue;
  163. const rings = u.rings;
  164. for (const fp of uniqueFps.array) {
  165. if (!rings.byFingerprint.has(fp)) continue;
  166. for (const r of rings.byFingerprint.get(fp)!) {
  167. ret.add(getRingStructure(u, rings.all[r], ctx.inputStructure));
  168. }
  169. }
  170. }
  171. }
  172. return ret.getSelection();
  173. }
  174. }
  175. export function querySelection(selection: StructureQuery, query: StructureQuery, inComplement: boolean = false): StructureQuery {
  176. return ctx => {
  177. const targetSel = selection(ctx);
  178. if (StructureSelection.structureCount(targetSel) === 0) return targetSel;
  179. const target = inComplement
  180. ? structureSubtract(ctx.inputStructure, StructureSelection.unionStructure(targetSel))
  181. : StructureSelection.unionStructure(targetSel);
  182. if (target.elementCount === 0) return StructureSelection.Empty(ctx.inputStructure);
  183. ctx.throwIfTimedOut();
  184. ctx.pushInputStructure(target);
  185. const result = query(ctx);
  186. ctx.popInputStructure();
  187. return StructureSelection.withInputStructure(result, ctx.inputStructure);
  188. }
  189. }