loci.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /**
  2. * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { StructureElement } from './structure'
  7. import { Link } from './structure/structure/unit/links'
  8. import { Shape, ShapeGroup } from './shape';
  9. import { Sphere3D } from '../mol-math/geometry';
  10. import { CentroidHelper } from '../mol-math/geometry/centroid-helper';
  11. import { Vec3 } from '../mol-math/linear-algebra';
  12. import { OrderedSet } from '../mol-data/int';
  13. import { Structure } from './structure/structure';
  14. /** A Loci that includes every loci */
  15. export const EveryLoci = { kind: 'every-loci' as 'every-loci' }
  16. export type EveryLoci = typeof EveryLoci
  17. export function isEveryLoci(x: any): x is EveryLoci {
  18. return !!x && x.kind === 'every-loci';
  19. }
  20. /** A Loci that is empty */
  21. export const EmptyLoci = { kind: 'empty-loci' as 'empty-loci' }
  22. export type EmptyLoci = typeof EmptyLoci
  23. export function isEmptyLoci(x: any): x is EmptyLoci {
  24. return !!x && x.kind === 'empty-loci';
  25. }
  26. /** A generic data loci */
  27. export interface DataLoci {
  28. readonly kind: 'data-loci',
  29. readonly data: any,
  30. readonly tag: string
  31. readonly indices: OrderedSet<number>
  32. }
  33. export function isDataLoci(x: any): x is DataLoci {
  34. return !!x && x.kind === 'data-loci';
  35. }
  36. export function areDataLociEqual(a: DataLoci, b: DataLoci) {
  37. return a.data === b.data && a.tag === b.tag && OrderedSet.areEqual(a.indices, b.indices)
  38. }
  39. export function isDataLociEmpty(loci: DataLoci) {
  40. return OrderedSet.size(loci.indices) === 0 ? true : false
  41. }
  42. export function createDataLoci(data: any, tag: string, indices: OrderedSet<number>): DataLoci {
  43. return { kind: 'data-loci', data, tag, indices }
  44. }
  45. export { Loci }
  46. type Loci = StructureElement.Loci | Structure.Loci | Link.Loci | EveryLoci | EmptyLoci | DataLoci | Shape.Loci | ShapeGroup.Loci
  47. namespace Loci {
  48. export function areEqual(lociA: Loci, lociB: Loci) {
  49. if (isEveryLoci(lociA) && isEveryLoci(lociB)) return true
  50. if (isEmptyLoci(lociA) && isEmptyLoci(lociB)) return true
  51. if (isDataLoci(lociA) && isDataLoci(lociB)) {
  52. return areDataLociEqual(lociA, lociB)
  53. }
  54. if (Structure.isLoci(lociA) && Structure.isLoci(lociB)) {
  55. return Structure.areLociEqual(lociA, lociB)
  56. }
  57. if (StructureElement.Loci.is(lociA) && StructureElement.Loci.is(lociB)) {
  58. return StructureElement.Loci.areEqual(lociA, lociB)
  59. }
  60. if (Link.isLoci(lociA) && Link.isLoci(lociB)) {
  61. return Link.areLociEqual(lociA, lociB)
  62. }
  63. if (Shape.isLoci(lociA) && Shape.isLoci(lociB)) {
  64. return Shape.areLociEqual(lociA, lociB)
  65. }
  66. if (ShapeGroup.isLoci(lociA) && ShapeGroup.isLoci(lociB)) {
  67. return ShapeGroup.areLociEqual(lociA, lociB)
  68. }
  69. return false
  70. }
  71. export function isEmpty(loci: Loci): boolean {
  72. if (isEveryLoci(loci)) return false
  73. if (isEmptyLoci(loci)) return true
  74. if (isDataLoci(loci)) return isDataLociEmpty(loci)
  75. if (Structure.isLoci(loci)) return Structure.isLociEmpty(loci)
  76. if (StructureElement.Loci.is(loci)) return StructureElement.Loci.isEmpty(loci)
  77. if (Link.isLoci(loci)) return Link.isLociEmpty(loci)
  78. if (Shape.isLoci(loci)) return Shape.isLociEmpty(loci)
  79. if (ShapeGroup.isLoci(loci)) return ShapeGroup.isLociEmpty(loci)
  80. return false
  81. }
  82. export function remap<T>(loci: Loci, data: T) {
  83. if (data instanceof Structure) {
  84. if (StructureElement.Loci.is(loci)) {
  85. loci = StructureElement.Loci.remap(loci, data)
  86. } else if (Link.isLoci(loci)) {
  87. loci = Link.remapLoci(loci, data)
  88. }
  89. }
  90. return loci
  91. }
  92. const sphereHelper = new CentroidHelper(), tempPos = Vec3.zero();
  93. export function getBoundingSphere(loci: Loci, boundingSphere?: Sphere3D): Sphere3D | undefined {
  94. if (loci.kind === 'every-loci' || loci.kind === 'empty-loci') return void 0;
  95. if (!boundingSphere) boundingSphere = Sphere3D()
  96. sphereHelper.reset();
  97. if (loci.kind === 'structure-loci') {
  98. return Sphere3D.copy(boundingSphere, loci.structure.boundary.sphere)
  99. } else if (loci.kind === 'element-loci') {
  100. return StructureElement.Loci.getBoundary(loci).sphere;
  101. } else if (loci.kind === 'link-loci') {
  102. for (const e of loci.links) {
  103. e.aUnit.conformation.position(e.aUnit.elements[e.aIndex], tempPos);
  104. sphereHelper.includeStep(tempPos);
  105. e.bUnit.conformation.position(e.bUnit.elements[e.bIndex], tempPos);
  106. sphereHelper.includeStep(tempPos);
  107. }
  108. sphereHelper.finishedIncludeStep();
  109. for (const e of loci.links) {
  110. e.aUnit.conformation.position(e.aUnit.elements[e.aIndex], tempPos);
  111. sphereHelper.radiusStep(tempPos);
  112. e.aUnit.conformation.position(e.bUnit.elements[e.bIndex], tempPos);
  113. sphereHelper.radiusStep(tempPos);
  114. }
  115. } else if (loci.kind === 'shape-loci') {
  116. // TODO
  117. return void 0;
  118. } else if (loci.kind === 'group-loci') {
  119. // TODO
  120. return void 0;
  121. } else if (loci.kind === 'data-loci') {
  122. // TODO maybe add loci.getBoundingSphere()???
  123. return void 0;
  124. }
  125. Vec3.copy(boundingSphere.center, sphereHelper.center)
  126. boundingSphere.radius = Math.sqrt(sphereHelper.radiusSq)
  127. return boundingSphere
  128. }
  129. const tmpSphere3D = Sphere3D.zero()
  130. export function getCenter(loci: Loci, center?: Vec3): Vec3 | undefined {
  131. const boundingSphere = getBoundingSphere(loci, tmpSphere3D)
  132. return boundingSphere ? Vec3.copy(center || Vec3.zero(), boundingSphere.center) : undefined
  133. }
  134. }