util.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. /**
  2. * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { Sphere3D } from '../../mol-math/geometry'
  7. import { Vec3 } from '../../mol-math/linear-algebra'
  8. import { Epos14, Epos98 } from '../../mol-math/geometry/epos-helper';
  9. export function calculateTextureInfo (n: number, itemSize: number) {
  10. const sqN = Math.sqrt(n)
  11. let width = Math.ceil(sqN)
  12. width = width + (itemSize - (width % itemSize)) % itemSize
  13. const height = width > 0 ? Math.ceil(n / width) : 0
  14. return { width, height, length: width * height * itemSize }
  15. }
  16. export interface TextureImage<T extends Uint8Array | Float32Array> {
  17. readonly array: T
  18. readonly width: number
  19. readonly height: number
  20. }
  21. export interface TextureVolume<T extends Uint8Array | Float32Array> {
  22. readonly array: T
  23. readonly width: number
  24. readonly height: number
  25. readonly depth: number
  26. }
  27. export function createTextureImage<T extends Uint8Array | Float32Array>(n: number, itemSize: number, arrayCtor: new (length: number) => T, array?: T): TextureImage<T> {
  28. const { length, width, height } = calculateTextureInfo(n, itemSize)
  29. array = array && array.length >= length ? array : new arrayCtor(length)
  30. return { array, width, height }
  31. }
  32. export function printTextureImage(textureImage: TextureImage<any>, scale = 1) {
  33. const { array, width, height } = textureImage
  34. const itemSize = array.length / (width * height)
  35. const data = new Uint8ClampedArray(width * height * 4)
  36. if (itemSize === 1) {
  37. for (let y = 0; y < height; ++y) {
  38. for (let x = 0; x < width; ++x) {
  39. data[(y * width + x) * 4 + 3] = array[y * width + x]
  40. }
  41. }
  42. } else if (itemSize === 4) {
  43. data.set(array)
  44. } else {
  45. console.warn(`itemSize '${itemSize}' not supported`)
  46. }
  47. return printImageData(new ImageData(data, width, height), scale)
  48. }
  49. export function printImageData(imageData: ImageData, scale = 1, pixelated = false) {
  50. const canvas = document.createElement('canvas')
  51. canvas.width = imageData.width
  52. canvas.height = imageData.height
  53. const ctx = canvas.getContext('2d')
  54. if (!ctx) throw new Error('Could not create canvas 2d context')
  55. ctx.putImageData(imageData, 0, 0)
  56. canvas.toBlob(imgBlob => {
  57. const objectURL = window.URL.createObjectURL(imgBlob)
  58. const img = document.createElement('img')
  59. img.src = objectURL
  60. img.style.width = imageData.width * scale + 'px'
  61. img.style.height = imageData.height * scale + 'px';
  62. if (pixelated) (img.style as any).imageRendering = 'pixelated' // supported only in Chrome
  63. img.style.position = 'absolute'
  64. img.style.top = '0px'
  65. img.style.left = '0px'
  66. img.style.border = 'solid grey'
  67. document.body.appendChild(img)
  68. }, 'image/png')
  69. }
  70. //
  71. const v = Vec3.zero()
  72. const eposHelper14 = Epos14()
  73. const eposHelper98 = Epos98()
  74. export function calculateInvariantBoundingSphere(position: Float32Array, positionCount: number, stepFactor: number): Sphere3D {
  75. const step = stepFactor * 3
  76. eposHelper14.reset()
  77. for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
  78. Vec3.fromArray(v, position, i)
  79. eposHelper14.includeStep(v)
  80. }
  81. eposHelper14.finishedIncludeStep()
  82. for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
  83. Vec3.fromArray(v, position, i)
  84. eposHelper14.radiusStep(v)
  85. }
  86. return eposHelper14.getSphere()
  87. }
  88. export function calculateTransformBoundingSphere(invariantBoundingSphere: Sphere3D, transform: Float32Array, transformCount: number): Sphere3D {
  89. const { center, radius } = invariantBoundingSphere
  90. eposHelper98.reset()
  91. for (let i = 0, _i = transformCount; i < _i; ++i) {
  92. Vec3.transformMat4Offset(v, center, transform, 0, 0, i * 16)
  93. eposHelper98.includeSphereStep(v, radius)
  94. }
  95. eposHelper98.finishedIncludeStep()
  96. for (let i = 0, _i = transformCount; i < _i; ++i) {
  97. Vec3.transformMat4Offset(v, center, transform, 0, 0, i * 16)
  98. eposHelper98.radiusSphereStep(v, radius)
  99. }
  100. return eposHelper98.getSphere()
  101. }
  102. export function calculateBoundingSphere(position: Float32Array, positionCount: number, transform: Float32Array, transformCount: number, padding = 0, stepFactor = 1): { boundingSphere: Sphere3D, invariantBoundingSphere: Sphere3D } {
  103. const invariantBoundingSphere = calculateInvariantBoundingSphere(position, positionCount, stepFactor)
  104. const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform, transformCount)
  105. Sphere3D.expand(boundingSphere, boundingSphere, padding)
  106. Sphere3D.expand(invariantBoundingSphere, invariantBoundingSphere, padding)
  107. return { boundingSphere, invariantBoundingSphere }
  108. }