element.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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 { Vec3 } from '../../../../mol-math/linear-algebra';
  7. import { Unit, StructureElement, Structure } from '../../../../mol-model/structure';
  8. import { Loci, EmptyLoci } from '../../../../mol-model/loci';
  9. import { Interval, OrderedSet } from '../../../../mol-data/int';
  10. import { Mesh } from '../../../../mol-geo/geometry/mesh/mesh';
  11. import { sphereVertexCount } from '../../../../mol-geo/primitive/sphere';
  12. import { MeshBuilder } from '../../../../mol-geo/geometry/mesh/mesh-builder';
  13. import { addSphere } from '../../../../mol-geo/geometry/mesh/builder/sphere';
  14. import { PickingId } from '../../../../mol-geo/geometry/picking';
  15. import { LocationIterator } from '../../../../mol-geo/util/location-iterator';
  16. import { VisualContext } from '../../../../mol-repr/visual';
  17. import { Theme } from '../../../../mol-theme/theme';
  18. import { StructureGroup } from '../../../../mol-repr/structure/units-visual';
  19. import { Spheres } from '../../../../mol-geo/geometry/spheres/spheres';
  20. import { SpheresBuilder } from '../../../../mol-geo/geometry/spheres/spheres-builder';
  21. import { isHydrogen } from './common';
  22. export interface ElementSphereMeshProps {
  23. detail: number,
  24. sizeFactor: number,
  25. ignoreHydrogens: boolean,
  26. }
  27. export function createElementSphereMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: ElementSphereMeshProps, mesh?: Mesh): Mesh {
  28. const { detail, sizeFactor } = props
  29. const { elements } = unit;
  30. const elementCount = elements.length;
  31. const vertexCount = elementCount * sphereVertexCount(detail)
  32. const builderState = MeshBuilder.createState(vertexCount, vertexCount / 2, mesh)
  33. const v = Vec3.zero()
  34. const pos = unit.conformation.invariantPosition
  35. const l = StructureElement.create()
  36. l.unit = unit
  37. for (let i = 0; i < elementCount; i++) {
  38. if (props.ignoreHydrogens && isHydrogen(unit, elements[i])) continue
  39. l.element = elements[i]
  40. pos(elements[i], v)
  41. builderState.currentGroup = i
  42. addSphere(builderState, v, theme.size.size(l) * sizeFactor, detail)
  43. }
  44. return MeshBuilder.getMesh(builderState)
  45. }
  46. export interface ElementSphereImpostorProps {
  47. ignoreHydrogens: boolean,
  48. }
  49. export function createElementSphereImpostor(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: ElementSphereImpostorProps, spheres?: Spheres): Spheres {
  50. const { elements } = unit;
  51. const elementCount = elements.length;
  52. const builder = SpheresBuilder.create(elementCount, elementCount / 2, spheres)
  53. const v = Vec3.zero()
  54. const pos = unit.conformation.invariantPosition
  55. for (let i = 0; i < elementCount; i++) {
  56. if (props.ignoreHydrogens && isHydrogen(unit, elements[i])) continue
  57. pos(elements[i], v)
  58. builder.add(v[0], v[1], v[2], i)
  59. }
  60. return builder.getSpheres()
  61. }
  62. export function eachElement(loci: Loci, structureGroup: StructureGroup, apply: (interval: Interval) => boolean) {
  63. let changed = false
  64. if (!StructureElement.isLoci(loci)) return false
  65. const { structure, group } = structureGroup
  66. if (!Structure.areEquivalent(loci.structure, structure)) return false
  67. const elementCount = group.elements.length
  68. for (const e of loci.elements) {
  69. const unitIdx = group.unitIndexMap.get(e.unit.id)
  70. if (unitIdx !== undefined) {
  71. if (Interval.is(e.indices)) {
  72. const start = unitIdx * elementCount + Interval.start(e.indices)
  73. const end = unitIdx * elementCount + Interval.end(e.indices)
  74. if (apply(Interval.ofBounds(start, end))) changed = true
  75. } else {
  76. for (let i = 0, _i = e.indices.length; i < _i; i++) {
  77. const idx = unitIdx * elementCount + e.indices[i]
  78. if (apply(Interval.ofSingleton(idx))) changed = true
  79. }
  80. }
  81. }
  82. }
  83. return changed
  84. }
  85. export function getElementLoci(pickingId: PickingId, structureGroup: StructureGroup, id: number) {
  86. const { objectId, instanceId, groupId } = pickingId
  87. if (id === objectId) {
  88. const { structure, group } = structureGroup
  89. const unit = group.units[instanceId]
  90. const indices = OrderedSet.ofSingleton(groupId as StructureElement.UnitIndex)
  91. return StructureElement.Loci(structure, [{ unit, indices }])
  92. }
  93. return EmptyLoci
  94. }
  95. //
  96. export function eachSerialElement(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean) {
  97. let changed = false
  98. if (!StructureElement.isLoci(loci)) return false
  99. if (!Structure.areEquivalent(loci.structure, structure)) return false
  100. const { unitElementCount } = structure.serialMapping
  101. for (const e of loci.elements) {
  102. const unitIdx = structure.unitIndexMap.get(e.unit.id)
  103. if (unitIdx !== undefined) {
  104. if (Interval.is(e.indices)) {
  105. const start = unitElementCount[unitIdx] + Interval.start(e.indices)
  106. const end = unitElementCount[unitIdx] + Interval.end(e.indices)
  107. if (apply(Interval.ofBounds(start, end))) changed = true
  108. } else {
  109. for (let i = 0, _i = e.indices.length; i < _i; i++) {
  110. const idx = unitElementCount[unitIdx] + e.indices[i]
  111. if (apply(Interval.ofSingleton(idx))) changed = true
  112. }
  113. }
  114. }
  115. }
  116. return changed
  117. }
  118. export function getSerialElementLoci(pickingId: PickingId, structure: Structure, id: number) {
  119. const { objectId, groupId } = pickingId
  120. if (id === objectId) {
  121. const { unitIndices, unitElementCount } = structure.serialMapping
  122. const unitIdx = unitIndices[groupId]
  123. const unit = structure.units[unitIdx]
  124. const idx = groupId - unitElementCount[unitIdx]
  125. const indices = OrderedSet.ofSingleton(idx as StructureElement.UnitIndex)
  126. return StructureElement.Loci(structure, [{ unit, indices }])
  127. }
  128. return EmptyLoci
  129. }
  130. //
  131. export namespace ElementIterator {
  132. export function fromGroup(group: Unit.SymmetryGroup): LocationIterator {
  133. const groupCount = group.elements.length
  134. const instanceCount = group.units.length
  135. const location = StructureElement.create()
  136. const getLocation = (groupIndex: number, instanceIndex: number) => {
  137. const unit = group.units[instanceIndex]
  138. location.unit = unit
  139. location.element = unit.elements[groupIndex]
  140. return location
  141. }
  142. return LocationIterator(groupCount, instanceCount, getLocation)
  143. }
  144. export function fromStructure(structure: Structure): LocationIterator {
  145. const { units, elementCount } = structure
  146. const groupCount = elementCount
  147. const instanceCount = 1
  148. const { unitIndices, elementIndices } = structure.serialMapping
  149. const location = StructureElement.create()
  150. const getLocation = (groupIndex: number) => {
  151. location.unit = units[unitIndices[groupIndex]]
  152. location.element = elementIndices[groupIndex]
  153. return location
  154. }
  155. return LocationIterator(groupCount, instanceCount, getLocation, true)
  156. }
  157. }