features.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /**
  2. * Copyright (c) 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, Unit, Structure } from '../../../mol-model/structure/structure';
  7. import { ChunkedArray } from '../../../mol-data/util';
  8. import { GridLookup3D } from '../../../mol-math/geometry';
  9. import { OrderedSet } from '../../../mol-data/int';
  10. import { FeatureGroup, FeatureType } from './common';
  11. import { ValenceModelProvider } from '../valence-model';
  12. export { Features }
  13. interface Features {
  14. /** number of features */
  15. readonly count: number
  16. /** center x coordinate, in invariant coordinate space */
  17. readonly x: ArrayLike<number>
  18. /** center y coordinate, in invariant coordinate space */
  19. readonly y: ArrayLike<number>
  20. /** center z coordinate, in invariant coordinate space */
  21. readonly z: ArrayLike<number>
  22. readonly types: ArrayLike<FeatureType>
  23. readonly groups: ArrayLike<FeatureGroup>
  24. readonly offsets: ArrayLike<number>
  25. /** elements of this feature, range for feature i is offsets[i] to offsets[i + 1] */
  26. readonly members: ArrayLike<StructureElement.UnitIndex>
  27. /** lookup3d based on center coordinates, in invariant coordinate space */
  28. readonly lookup3d: GridLookup3D
  29. /** maps unit elements to features, range for unit element i is offsets[i] to offsets[i + 1] */
  30. readonly elementsIndex: Features.ElementsIndex
  31. }
  32. namespace Features {
  33. /** Index into Features data arrays */
  34. export type FeatureIndex = { readonly '@type': 'feature-index' } & number
  35. /** maps unit elements to features, range for unit element i is offsets[i] to offsets[i + 1] */
  36. export type ElementsIndex = {
  37. /** feature indices */
  38. readonly indices: ArrayLike<FeatureIndex>
  39. /** range for unit element i is offsets[i] to offsets[i + 1] */
  40. readonly offsets: ArrayLike<number>
  41. }
  42. export type Data = {
  43. count: number
  44. x: ArrayLike<number>
  45. y: ArrayLike<number>
  46. z: ArrayLike<number>
  47. types: ArrayLike<FeatureType>
  48. groups: ArrayLike<FeatureGroup>
  49. offsets: ArrayLike<number>
  50. members: ArrayLike<StructureElement.UnitIndex>
  51. }
  52. export function createElementsIndex(data: Data, elementsCount: number): ElementsIndex {
  53. const offsets = new Int32Array(elementsCount + 1)
  54. const bucketFill = new Int32Array(elementsCount)
  55. const bucketSizes = new Int32Array(elementsCount)
  56. const { members, count, offsets: featureOffsets } = data
  57. for (let i = 0, il = featureOffsets[count]; i < il; ++i) ++bucketSizes[members[i]]
  58. let offset = 0
  59. for (let i = 0; i < elementsCount; i++) {
  60. offsets[i] = offset
  61. offset += bucketSizes[i]
  62. }
  63. offsets[elementsCount] = offset
  64. const indices = new Int32Array(offset)
  65. for (let i = 0; i < count; ++i) {
  66. for (let j = featureOffsets[i], jl = featureOffsets[i + 1]; j < jl; ++j) {
  67. const a = members[j]
  68. const oa = offsets[a] + bucketFill[a]
  69. indices[oa] = i
  70. ++bucketFill[a]
  71. }
  72. }
  73. return { indices: indices as unknown as ArrayLike<FeatureIndex>, offsets }
  74. }
  75. export function create(elementsCount: number, data: Data): Features {
  76. let lookup3d: GridLookup3D
  77. let elementsIndex: ElementsIndex
  78. return {
  79. ...data,
  80. get lookup3d() {
  81. return lookup3d || (lookup3d = GridLookup3D({ x: data.x, y: data.y, z: data.z, indices: OrderedSet.ofBounds(0, data.count) }))
  82. },
  83. get elementsIndex() {
  84. return elementsIndex || (elementsIndex = createElementsIndex(data, elementsCount))
  85. },
  86. }
  87. }
  88. export interface Info {
  89. unit: Unit.Atomic,
  90. types: ArrayLike<FeatureType>,
  91. feature: number,
  92. x: ArrayLike<number>
  93. y: ArrayLike<number>
  94. z: ArrayLike<number>
  95. members: ArrayLike<StructureElement.UnitIndex>,
  96. offsets: ArrayLike<number>,
  97. idealGeometry: Int8Array
  98. }
  99. export function Info(structure: Structure, unit: Unit.Atomic, features: Features): Info {
  100. const valenceModel = ValenceModelProvider.getValue(structure).value
  101. if (!valenceModel || !valenceModel.has(unit.id)) throw new Error('valence model required')
  102. return {
  103. unit,
  104. types: features.types,
  105. feature: -1,
  106. x: features.x,
  107. y: features.y,
  108. z: features.z,
  109. members: features.members,
  110. offsets: features.offsets,
  111. idealGeometry: valenceModel.get(unit.id)!.idealGeometry
  112. }
  113. }
  114. export interface Provider {
  115. name: string
  116. add: (structure: Structure, unit: Unit.Atomic, featuresBuilder: FeaturesBuilder) => void
  117. }
  118. }
  119. export { FeaturesBuilder }
  120. interface FeaturesBuilder {
  121. startState: () => void
  122. pushMember: (x: number, y: number, z: number, member: StructureElement.UnitIndex) => void
  123. finishState: (type: FeatureType, group: FeatureGroup) => void
  124. add: (type: FeatureType, group: FeatureGroup, x: number, y: number, z: number, member: StructureElement.UnitIndex) => void
  125. getFeatures: (elementsCount: number) => Features
  126. }
  127. namespace FeaturesBuilder {
  128. interface State { x: number, y: number, z: number, offset: number, count: number }
  129. export function create(initialCount = 2048, chunkSize = 1024, features?: Features): FeaturesBuilder {
  130. const xCenters = ChunkedArray.create(Float32Array, 1, chunkSize, features ? features.x : initialCount)
  131. const yCenters = ChunkedArray.create(Float32Array, 1, chunkSize, features ? features.y : initialCount)
  132. const zCenters = ChunkedArray.create(Float32Array, 1, chunkSize, features ? features.z : initialCount)
  133. const types = ChunkedArray.create(Uint8Array, 1, chunkSize, features ? features.types : initialCount)
  134. const groups = ChunkedArray.create(Uint8Array, 1, chunkSize, features ? features.groups : initialCount)
  135. const offsets = ChunkedArray.create(Uint32Array, 1, chunkSize, features ? features.offsets : initialCount)
  136. const members = ChunkedArray.create(Uint32Array, 1, chunkSize, features ? features.members : initialCount)
  137. const state: State = { x: 0, y: 0, z: 0, offset: 0, count: 0 }
  138. return {
  139. startState: () => {
  140. state.x = 0
  141. state.y = 0
  142. state.z = 0
  143. state.offset = members.elementCount
  144. state.count = 0
  145. },
  146. pushMember: (x: number, y: number, z: number, member: StructureElement.UnitIndex) => {
  147. ChunkedArray.add(members, member)
  148. state.x += x
  149. state.y += y
  150. state.z += z
  151. state.count += 1
  152. },
  153. finishState: (type: FeatureType, group: FeatureGroup) => {
  154. const { count } = state
  155. if (count === 0) return
  156. ChunkedArray.add(types, type)
  157. ChunkedArray.add(groups, group)
  158. ChunkedArray.add(xCenters, state.x / count)
  159. ChunkedArray.add(yCenters, state.y / count)
  160. ChunkedArray.add(zCenters, state.z / count)
  161. ChunkedArray.add(offsets, state.offset)
  162. },
  163. add: (type: FeatureType, group: FeatureGroup, x: number, y: number, z: number, member: StructureElement.UnitIndex) => {
  164. ChunkedArray.add(types, type)
  165. ChunkedArray.add(groups, group)
  166. ChunkedArray.add(xCenters, x)
  167. ChunkedArray.add(yCenters, y)
  168. ChunkedArray.add(zCenters, z)
  169. ChunkedArray.add(offsets, members.elementCount)
  170. ChunkedArray.add(members, member)
  171. },
  172. getFeatures: (elementsCount: number) => {
  173. ChunkedArray.add(offsets, members.elementCount)
  174. const x = ChunkedArray.compact(xCenters, true) as ArrayLike<number>
  175. const y = ChunkedArray.compact(yCenters, true) as ArrayLike<number>
  176. const z = ChunkedArray.compact(zCenters, true) as ArrayLike<number>
  177. const count = xCenters.elementCount
  178. return Features.create(elementsCount, {
  179. x, y, z, count,
  180. types: ChunkedArray.compact(types, true) as ArrayLike<FeatureType>,
  181. groups: ChunkedArray.compact(groups, true) as ArrayLike<FeatureGroup>,
  182. offsets: ChunkedArray.compact(offsets, true) as ArrayLike<number>,
  183. members: ChunkedArray.compact(members, true) as ArrayLike<StructureElement.UnitIndex>,
  184. })
  185. }
  186. }
  187. }
  188. }