generators.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. /**
  2. * Copyright (c) 2017-2019 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 { StructureQuery } from '../query'
  8. import { StructureSelection } from '../selection'
  9. import { Unit, StructureProperties as P, StructureElement } from '../../structure'
  10. import { Segmentation, SortedArray } from '../../../../mol-data/int'
  11. import { LinearGroupingBuilder } from '../utils/builders';
  12. import { QueryPredicate, QueryFn, QueryContextView } from '../context';
  13. import { UnitRing } from '../../structure/unit/rings';
  14. import Structure from '../../structure/structure';
  15. import { ElementIndex } from '../../model';
  16. import { UniqueArray } from '../../../../mol-data/generic';
  17. import { structureSubtract } from '../utils/structure-set';
  18. export const none: StructureQuery = ctx => StructureSelection.Sequence(ctx.inputStructure, []);
  19. export const all: StructureQuery = ctx => StructureSelection.Singletons(ctx.inputStructure, ctx.inputStructure);
  20. export interface AtomsQueryParams {
  21. /** Query to be executed for each unit once */
  22. unitTest: QueryPredicate,
  23. /** Query to be executed for each entity once */
  24. entityTest: QueryPredicate,
  25. /** Query to be executed for each chain once */
  26. chainTest: QueryPredicate,
  27. /** Query to be executed for each residue (or coarse element) once */
  28. residueTest: QueryPredicate,
  29. /** Query to be executed for each atom */
  30. atomTest: QueryPredicate,
  31. groupBy: QueryFn
  32. }
  33. export function residues(params?: Partial<AtomsQueryParams>) { return atoms({ ...params, groupBy: ctx => P.residue.key(ctx.element) }); }
  34. export function chains(params?: Partial<AtomsQueryParams>) { return atoms({ ...params, groupBy: ctx => P.chain.key(ctx.element) }); }
  35. function _true(ctx: QueryContextView) { return true; }
  36. function _zero(ctx: QueryContextView) { return 0; }
  37. export function atoms(params?: Partial<AtomsQueryParams>): StructureQuery {
  38. if (!params || (!params.atomTest && !params.residueTest && !params.chainTest && !params.entityTest && !params.unitTest && !params.groupBy)) return all;
  39. if (!!params.atomTest && !params.residueTest && !params.chainTest && !params.entityTest && !params.unitTest && !params.groupBy) return atomGroupsLinear(params.atomTest);
  40. const normalized: AtomsQueryParams = {
  41. unitTest: params.unitTest || _true,
  42. entityTest: params.entityTest || _true,
  43. chainTest: params.chainTest || _true,
  44. residueTest: params.residueTest || _true,
  45. atomTest: params.atomTest || _true,
  46. groupBy: params.groupBy || _zero,
  47. };
  48. if (!params.groupBy) return atomGroupsSegmented(normalized)
  49. return atomGroupsGrouped(normalized);
  50. }
  51. function atomGroupsLinear(atomTest: QueryPredicate): StructureQuery {
  52. return function query_atomGroupsLinear(ctx) {
  53. const { inputStructure } = ctx;
  54. const { units } = inputStructure;
  55. const l = ctx.pushCurrentElement();
  56. const builder = inputStructure.subsetBuilder(true);
  57. for (const unit of units) {
  58. l.unit = unit;
  59. const elements = unit.elements;
  60. builder.beginUnit(unit.id);
  61. for (let j = 0, _j = elements.length; j < _j; j++) {
  62. l.element = elements[j];
  63. if (atomTest(ctx)) builder.addElement(l.element);
  64. }
  65. builder.commitUnit();
  66. ctx.throwIfTimedOut();
  67. }
  68. ctx.popCurrentElement();
  69. return StructureSelection.Singletons(inputStructure, builder.getStructure());
  70. };
  71. }
  72. function atomGroupsSegmented({ unitTest, entityTest, chainTest, residueTest, atomTest }: AtomsQueryParams): StructureQuery {
  73. return function query_atomGroupsSegmented(ctx) {
  74. const { inputStructure } = ctx;
  75. const { units } = inputStructure;
  76. const l = ctx.pushCurrentElement();
  77. const builder = inputStructure.subsetBuilder(true);
  78. for (const unit of units) {
  79. l.unit = unit;
  80. if (!unitTest(ctx)) continue;
  81. const { elements, model } = unit;
  82. builder.beginUnit(unit.id);
  83. if (unit.kind === Unit.Kind.Atomic) {
  84. const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, elements);
  85. const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, elements);
  86. while (chainsIt.hasNext) {
  87. const chainSegment = chainsIt.move();
  88. l.element = elements[chainSegment.start];
  89. // test entity and chain
  90. if (!entityTest(ctx) || !chainTest(ctx)) continue;
  91. residuesIt.setSegment(chainSegment);
  92. while (residuesIt.hasNext) {
  93. const residueSegment = residuesIt.move();
  94. l.element = elements[residueSegment.start];
  95. // test residue
  96. if (!residueTest(ctx)) continue;
  97. for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) {
  98. l.element = elements[j];
  99. // test atom
  100. if (atomTest(ctx)) {
  101. builder.addElement(l.element);
  102. }
  103. }
  104. }
  105. }
  106. } else {
  107. const { chainElementSegments } = Unit.Kind.Spheres ? model.coarseHierarchy.spheres : model.coarseHierarchy.gaussians
  108. const chainsIt = Segmentation.transientSegments(chainElementSegments, elements);
  109. while (chainsIt.hasNext) {
  110. const chainSegment = chainsIt.move();
  111. l.element = elements[chainSegment.start];
  112. // test entity and chain
  113. if (!entityTest(ctx) || !chainTest(ctx)) continue;
  114. for (let j = chainSegment.start, _j = chainSegment.end; j < _j; j++) {
  115. l.element = elements[j];
  116. // test residue/coarse element
  117. if (residueTest(ctx)) {
  118. builder.addElement(l.element);
  119. }
  120. }
  121. }
  122. }
  123. builder.commitUnit();
  124. ctx.throwIfTimedOut();
  125. }
  126. ctx.popCurrentElement();
  127. return StructureSelection.Singletons(inputStructure, builder.getStructure());
  128. };
  129. }
  130. function atomGroupsGrouped({ unitTest, entityTest, chainTest, residueTest, atomTest, groupBy }: AtomsQueryParams): StructureQuery {
  131. return function query_atomGroupsGrouped(ctx) {
  132. const { inputStructure } = ctx;
  133. const { units } = inputStructure;
  134. const l = ctx.pushCurrentElement();
  135. const builder = new LinearGroupingBuilder(inputStructure);
  136. for (const unit of units) {
  137. l.unit = unit;
  138. if (!unitTest(ctx)) continue;
  139. const { elements, model } = unit;
  140. if (unit.kind === Unit.Kind.Atomic) {
  141. const chainsIt = Segmentation.transientSegments(model.atomicHierarchy.chainAtomSegments, elements);
  142. const residuesIt = Segmentation.transientSegments(model.atomicHierarchy.residueAtomSegments, elements);
  143. while (chainsIt.hasNext) {
  144. const chainSegment = chainsIt.move();
  145. l.element = elements[chainSegment.start];
  146. // test entity and chain
  147. if (!entityTest(ctx) || !chainTest(ctx)) continue;
  148. residuesIt.setSegment(chainSegment);
  149. while (residuesIt.hasNext) {
  150. const residueSegment = residuesIt.move();
  151. l.element = elements[residueSegment.start];
  152. // test residue
  153. if (!residueTest(ctx)) continue;
  154. for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) {
  155. l.element = elements[j];
  156. // test atom
  157. if (atomTest(ctx)) {
  158. builder.add(groupBy(ctx), unit.id, l.element);
  159. }
  160. }
  161. }
  162. }
  163. } else {
  164. const { chainElementSegments } = Unit.Kind.Spheres ? model.coarseHierarchy.spheres : model.coarseHierarchy.gaussians
  165. const chainsIt = Segmentation.transientSegments(chainElementSegments, elements);
  166. while (chainsIt.hasNext) {
  167. const chainSegment = chainsIt.move();
  168. l.element = elements[chainSegment.start];
  169. // test entity and chain
  170. if (!entityTest(ctx) || !chainTest(ctx)) continue;
  171. for (let j = chainSegment.start, _j = chainSegment.end; j < _j; j++) {
  172. l.element = elements[j];
  173. // test residue/coarse element
  174. if (residueTest(ctx)) {
  175. builder.add(groupBy(ctx), unit.id, l.element);
  176. }
  177. }
  178. }
  179. }
  180. ctx.throwIfTimedOut();
  181. }
  182. ctx.popCurrentElement();
  183. return builder.getSelection();
  184. };
  185. }
  186. function getRingStructure(unit: Unit.Atomic, ring: UnitRing, inputStructure: Structure) {
  187. const elements = new Int32Array(ring.length) as any as ElementIndex[];
  188. for (let i = 0, _i = ring.length; i < _i; i++) elements[i] = unit.elements[ring[i]];
  189. return Structure.create([unit.getChild(SortedArray.ofSortedArray(elements))], { parent: inputStructure });
  190. }
  191. export function rings(fingerprints?: ArrayLike<UnitRing.Fingerprint>): StructureQuery {
  192. return function query_rings(ctx) {
  193. const { units } = ctx.inputStructure;
  194. const ret = StructureSelection.LinearBuilder(ctx.inputStructure);
  195. if (!fingerprints || fingerprints.length === 0) {
  196. for (const u of units) {
  197. if (!Unit.isAtomic(u)) continue;
  198. for (const r of u.rings.all) {
  199. ret.add(getRingStructure(u, r, ctx.inputStructure));
  200. }
  201. }
  202. } else {
  203. const uniqueFps = UniqueArray.create<UnitRing.Fingerprint, UnitRing.Fingerprint>();
  204. for (let i = 0; i < fingerprints.length; i++) UniqueArray.add(uniqueFps, fingerprints[i], fingerprints[i]);
  205. for (const u of units) {
  206. if (!Unit.isAtomic(u)) continue;
  207. const rings = u.rings;
  208. for (const fp of uniqueFps.array) {
  209. if (!rings.byFingerprint.has(fp)) continue;
  210. for (const r of rings.byFingerprint.get(fp)!) {
  211. ret.add(getRingStructure(u, rings.all[r], ctx.inputStructure));
  212. }
  213. }
  214. }
  215. }
  216. return ret.getSelection();
  217. }
  218. }
  219. export function querySelection(selection: StructureQuery, query: StructureQuery, inComplement: boolean = false): StructureQuery {
  220. return function query_querySelection(ctx) {
  221. const targetSel = selection(ctx);
  222. if (StructureSelection.structureCount(targetSel) === 0) return targetSel;
  223. const target = inComplement
  224. ? structureSubtract(ctx.inputStructure, StructureSelection.unionStructure(targetSel))
  225. : StructureSelection.unionStructure(targetSel);
  226. if (target.elementCount === 0) return StructureSelection.Empty(ctx.inputStructure);
  227. ctx.throwIfTimedOut();
  228. ctx.pushInputStructure(target);
  229. const result = query(ctx);
  230. ctx.popInputStructure();
  231. return StructureSelection.withInputStructure(result, ctx.inputStructure);
  232. }
  233. }
  234. export function linkedAtomicPairs(linkTest?: QueryPredicate): StructureQuery {
  235. return function query_linkedAtomicPairs(ctx) {
  236. const structure = ctx.inputStructure;
  237. const interLinks = structure.interUnitBonds;
  238. // Note: each link is called twice, that's why we need the unique builder.
  239. const ret = StructureSelection.UniqueBuilder(ctx.inputStructure);
  240. ctx.pushCurrentLink();
  241. const atomicLink = ctx.atomicLink;
  242. atomicLink.setTestFn(linkTest);
  243. // Process intra unit links
  244. for (const unit of structure.units) {
  245. if (unit.kind !== Unit.Kind.Atomic) continue;
  246. const { offset: intraLinkOffset, b: intraLinkB, edgeProps: { flags, order } } = unit.links;
  247. atomicLink.a.unit = unit;
  248. atomicLink.b.unit = unit;
  249. for (let i = 0 as StructureElement.UnitIndex, _i = unit.elements.length; i < _i; i++) {
  250. atomicLink.aIndex = i as StructureElement.UnitIndex;
  251. atomicLink.a.element = unit.elements[i];
  252. // check intra unit links
  253. for (let lI = intraLinkOffset[i], _lI = intraLinkOffset[i + 1]; lI < _lI; lI++) {
  254. atomicLink.bIndex = intraLinkB[lI] as StructureElement.UnitIndex;
  255. atomicLink.b.element = unit.elements[intraLinkB[lI]];
  256. atomicLink.type = flags[lI];
  257. atomicLink.order = order[lI];
  258. // No need to "swap test" because each bond direction will be visited eventually.
  259. if (atomicLink.test(ctx, false)) {
  260. const b = structure.subsetBuilder(false);
  261. b.beginUnit(unit.id);
  262. b.addElement(atomicLink.a.element);
  263. b.addElement(atomicLink.b.element);
  264. b.commitUnit();
  265. ret.add(b.getStructure());
  266. }
  267. }
  268. }
  269. }
  270. // Process inter unit links
  271. for (const bond of interLinks.edges) {
  272. atomicLink.a.unit = bond.unitA;
  273. atomicLink.a.element = bond.unitA.elements[bond.indexA];
  274. atomicLink.aIndex = bond.indexA;
  275. atomicLink.b.unit = bond.unitB;
  276. atomicLink.b.element = bond.unitB.elements[bond.indexB];
  277. atomicLink.bIndex = bond.indexB;
  278. atomicLink.order = bond.props.order;
  279. atomicLink.type = bond.props.flag;
  280. // No need to "swap test" because each bond direction will be visited eventually.
  281. if (atomicLink.test(ctx, false)) {
  282. const b = structure.subsetBuilder(false);
  283. b.addToUnit(atomicLink.a.unit.id, atomicLink.a.element);
  284. b.addToUnit(atomicLink.b.unit.id, atomicLink.b.element);
  285. ret.add(b.getStructure());
  286. }
  287. }
  288. ctx.popCurrentLink();
  289. return ret.getSelection();
  290. };
  291. }