structure.ts 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /**
  2. * Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { IntMap, SortedArray, Iterator, Segmentation } from 'mol-data/int'
  7. import { UniqueArray } from 'mol-data/generic'
  8. import { SymmetryOperator } from 'mol-math/geometry/symmetry-operator'
  9. import { Model } from '../model'
  10. import { sort, arraySwap, hash1, sortArray } from 'mol-data/util';
  11. import Element from './element'
  12. import Unit from './unit'
  13. import { StructureLookup3D } from './util/lookup3d';
  14. import { CoarseElements } from '../model/properties/coarse';
  15. import { StructureSubsetBuilder } from './util/subset-builder';
  16. import { Queries } from '../query';
  17. import { InterUnitBonds, computeInterUnitBonds } from './unit/links';
  18. class Structure {
  19. readonly unitMap: IntMap<Unit>;
  20. readonly units: ReadonlyArray<Unit>;
  21. readonly elementCount: number;
  22. private _hashCode = 0;
  23. subsetBuilder(isSorted: boolean) {
  24. return new StructureSubsetBuilder(this, isSorted);
  25. }
  26. get hashCode() {
  27. if (this._hashCode !== 0) return this._hashCode;
  28. return this.computeHash();
  29. }
  30. private computeHash() {
  31. let hash = 23;
  32. for (let i = 0, _i = this.units.length; i < _i; i++) {
  33. const u = this.units[i];
  34. hash = (31 * hash + u.id) | 0;
  35. hash = (31 * hash + SortedArray.hashCode(u.elements)) | 0;
  36. }
  37. hash = (31 * hash + this.elementCount) | 0;
  38. hash = hash1(hash);
  39. this._hashCode = hash;
  40. return hash;
  41. }
  42. elementLocations(): Iterator<Element.Location> {
  43. return new Structure.ElementLocationIterator(this);
  44. }
  45. get boundary() {
  46. return this.lookup3d.boundary;
  47. }
  48. private _lookup3d?: StructureLookup3D = void 0;
  49. get lookup3d() {
  50. if (this._lookup3d) return this._lookup3d;
  51. this._lookup3d = new StructureLookup3D(this);
  52. return this._lookup3d;
  53. }
  54. private _links?: InterUnitBonds = void 0;
  55. get links() {
  56. if (this._links) return this._links;
  57. this._links = computeInterUnitBonds(this);
  58. return this._links;
  59. }
  60. constructor(units: ArrayLike<Unit>) {
  61. const map = IntMap.Mutable<Unit>();
  62. let elementCount = 0;
  63. let isSorted = true;
  64. let lastId = units.length > 0 ? units[0].id : 0;
  65. for (let i = 0, _i = units.length; i < _i; i++) {
  66. const u = units[i];
  67. map.set(u.id, u);
  68. elementCount += u.elements.length;
  69. if (u.id < lastId) isSorted = false;
  70. lastId = u.id;
  71. }
  72. if (!isSorted) sort(units, 0, units.length, cmpUnits, arraySwap)
  73. this.unitMap = map;
  74. this.units = units as ReadonlyArray<Unit>;
  75. this.elementCount = elementCount;
  76. }
  77. }
  78. function cmpUnits(units: ArrayLike<Unit>, i: number, j: number) { return units[i].id - units[j].id; }
  79. namespace Structure {
  80. export const Empty = new Structure([]);
  81. export function create(units: ReadonlyArray<Unit>): Structure { return new Structure(units); }
  82. /**
  83. * Construct a Structure from a model.
  84. *
  85. * Generally, a single unit corresponds to a single chain, with the exception
  86. * of consecutive "single atom chains".
  87. */
  88. export function ofModel(model: Model): Structure {
  89. const chains = model.atomicHierarchy.chainSegments;
  90. const builder = new StructureBuilder();
  91. for (let c = 0; c < chains.count; c++) {
  92. const start = chains.segments[c];
  93. // merge all consecutive "single atom chains"
  94. while (c + 1 < chains.count
  95. && chains.segments[c + 1] - chains.segments[c] === 1
  96. && chains.segments[c + 2] - chains.segments[c + 1] === 1) {
  97. c++;
  98. }
  99. const elements = SortedArray.ofBounds(start, chains.segments[c + 1]);
  100. builder.addUnit(Unit.Kind.Atomic, model, SymmetryOperator.Default, elements);
  101. }
  102. const cs = model.coarseHierarchy;
  103. if (cs.isDefined) {
  104. if (cs.spheres.count > 0) {
  105. addCoarseUnits(builder, model, model.coarseHierarchy.spheres, Unit.Kind.Spheres);
  106. }
  107. if (cs.gaussians.count > 0) {
  108. addCoarseUnits(builder, model, model.coarseHierarchy.gaussians, Unit.Kind.Gaussians);
  109. }
  110. }
  111. return builder.getStructure();
  112. }
  113. function addCoarseUnits(builder: StructureBuilder, model: Model, elements: CoarseElements, kind: Unit.Kind) {
  114. const { chainSegments } = elements;
  115. for (let cI = 0; cI < chainSegments.count; cI++) {
  116. const elements = SortedArray.ofBounds(chainSegments.segments[cI], chainSegments.segments[cI + 1]);
  117. builder.addUnit(kind, model, SymmetryOperator.Default, elements);
  118. }
  119. }
  120. export class StructureBuilder {
  121. private units: Unit[] = [];
  122. addUnit(kind: Unit.Kind, model: Model, operator: SymmetryOperator, elements: SortedArray): Unit {
  123. const unit = Unit.create(this.units.length, kind, model, operator, elements);
  124. this.units.push(unit);
  125. return unit;
  126. }
  127. addWithOperator(unit: Unit, operator: SymmetryOperator): Unit {
  128. const newUnit = unit.applyOperator(this.units.length, operator);
  129. this.units.push(newUnit);
  130. return newUnit;
  131. }
  132. getStructure(): Structure {
  133. return create(this.units);
  134. }
  135. get isEmpty() {
  136. return this.units.length === 0;
  137. }
  138. }
  139. export function Builder() { return new StructureBuilder(); }
  140. export function getModels(s: Structure) {
  141. const { units } = s;
  142. const arr = UniqueArray.create<Model['id'], Model>();
  143. for (const u of units) {
  144. UniqueArray.add(arr, u.model.id, u.model);
  145. }
  146. return arr.array;
  147. }
  148. export function hashCode(s: Structure) {
  149. return s.hashCode;
  150. }
  151. export function areEqual(a: Structure, b: Structure) {
  152. if (a.elementCount !== b.elementCount) return false;
  153. const len = a.units.length;
  154. if (len !== b.units.length) return false;
  155. for (let i = 0; i < len; i++) {
  156. if (a.units[i].id !== b.units[i].id) return false;
  157. }
  158. for (let i = 0; i < len; i++) {
  159. if (!SortedArray.areEqual(a.units[i].elements, b.units[i].elements)) return false;
  160. }
  161. return true;
  162. }
  163. export class ElementLocationIterator implements Iterator<Element.Location> {
  164. private current = Element.Location();
  165. private unitIndex = 0;
  166. private elements: SortedArray;
  167. private maxIdx = 0;
  168. private idx = -1;
  169. hasNext: boolean;
  170. move(): Element.Location {
  171. this.advance();
  172. this.current.element = this.elements[this.idx];
  173. return this.current;
  174. }
  175. private advance() {
  176. if (this.idx < this.maxIdx) {
  177. this.idx++;
  178. if (this.idx === this.maxIdx) this.hasNext = this.unitIndex + 1 < this.structure.units.length;
  179. return;
  180. }
  181. this.idx = 0;
  182. this.unitIndex++;
  183. if (this.unitIndex >= this.structure.units.length) {
  184. this.hasNext = false;
  185. return;
  186. }
  187. this.current.unit = this.structure.units[this.unitIndex];
  188. this.elements = this.current.unit.elements;
  189. this.maxIdx = this.elements.length - 1;
  190. }
  191. constructor(private structure: Structure) {
  192. this.hasNext = structure.elementCount > 0;
  193. if (this.hasNext) {
  194. this.elements = structure.units[0].elements;
  195. this.maxIdx = this.elements.length - 1;
  196. this.current.unit = structure.units[0];
  197. }
  198. }
  199. }
  200. export function getEntityKeys(structure: Structure) {
  201. const { units } = structure;
  202. const l = Element.Location();
  203. const keys = UniqueArray.create<number, number>();
  204. for (const unit of units) {
  205. const prop = unit.kind === Unit.Kind.Atomic ? Queries.props.entity.key : Queries.props.coarse.entityKey;
  206. l.unit = unit;
  207. const elements = unit.elements;
  208. const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainSegments, elements);
  209. while (chainsIt.hasNext) {
  210. const chainSegment = chainsIt.move();
  211. l.element = elements[chainSegment.start];
  212. const key = prop(l);
  213. UniqueArray.add(keys, key, key);
  214. }
  215. }
  216. sortArray(keys.array);
  217. return keys.array;
  218. }
  219. }
  220. export default Structure