util.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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, Mat4 } from '../../mol-math/linear-algebra';
  8. import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper';
  9. export function calculateTextureInfo (n: number, itemSize: number) {
  10. n = Math.max(n, 2); // observed issues with 1 pixel textures
  11. const sqN = Math.sqrt(n);
  12. let width = Math.ceil(sqN);
  13. width = width + (itemSize - (width % itemSize)) % itemSize;
  14. const height = width > 0 ? Math.ceil(n / width) : 0;
  15. return { width, height, length: width * height * itemSize };
  16. }
  17. export interface TextureImage<T extends Uint8Array | Float32Array> {
  18. readonly array: T
  19. readonly width: number
  20. readonly height: number
  21. readonly flipY?: boolean
  22. }
  23. export interface TextureVolume<T extends Uint8Array | Float32Array> {
  24. readonly array: T
  25. readonly width: number
  26. readonly height: number
  27. readonly depth: number
  28. }
  29. export function createTextureImage<T extends Uint8Array | Float32Array>(n: number, itemSize: number, arrayCtor: new (length: number) => T, array?: T): TextureImage<T> {
  30. const { length, width, height } = calculateTextureInfo(n, itemSize);
  31. array = array && array.length >= length ? array : new arrayCtor(length);
  32. return { array, width, height };
  33. }
  34. export function printTextureImage(textureImage: TextureImage<any>, scale = 1) {
  35. const { array, width, height } = textureImage;
  36. const itemSize = array.length / (width * height);
  37. const data = new Uint8ClampedArray(width * height * 4);
  38. if (itemSize === 1) {
  39. for (let y = 0; y < height; ++y) {
  40. for (let x = 0; x < width; ++x) {
  41. data[(y * width + x) * 4 + 3] = array[y * width + x];
  42. }
  43. }
  44. } else if (itemSize === 4) {
  45. data.set(array);
  46. } else {
  47. console.warn(`itemSize '${itemSize}' not supported`);
  48. }
  49. return printImageData(new ImageData(data, width, height), scale);
  50. }
  51. export function printImageData(imageData: ImageData, scale = 1, pixelated = false) {
  52. const canvas = document.createElement('canvas');
  53. canvas.width = imageData.width;
  54. canvas.height = imageData.height;
  55. const ctx = canvas.getContext('2d');
  56. if (!ctx) throw new Error('Could not create canvas 2d context');
  57. ctx.putImageData(imageData, 0, 0);
  58. canvas.toBlob(imgBlob => {
  59. const objectURL = window.URL.createObjectURL(imgBlob);
  60. const img = document.createElement('img');
  61. img.src = objectURL;
  62. img.style.width = imageData.width * scale + 'px';
  63. img.style.height = imageData.height * scale + 'px';
  64. if (pixelated) {
  65. // not supported in Firefox and IE
  66. img.style.imageRendering = 'pixelated';
  67. }
  68. img.style.position = 'absolute';
  69. img.style.top = '0px';
  70. img.style.left = '0px';
  71. img.style.border = 'solid grey';
  72. img.style.pointerEvents = 'none';
  73. document.body.appendChild(img);
  74. }, 'image/png');
  75. }
  76. //
  77. const v = Vec3();
  78. const boundaryHelperCoarse = new BoundaryHelper('14');
  79. const boundaryHelperFine = new BoundaryHelper('98');
  80. function getHelper(count: number) {
  81. return count > 100_000 ? boundaryHelperCoarse : boundaryHelperFine;
  82. }
  83. export function calculateInvariantBoundingSphere(position: Float32Array, positionCount: number, stepFactor: number): Sphere3D {
  84. const step = stepFactor * 3;
  85. const boundaryHelper = getHelper(positionCount);
  86. boundaryHelper.reset();
  87. for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
  88. Vec3.fromArray(v, position, i);
  89. boundaryHelper.includePosition(v);
  90. }
  91. boundaryHelper.finishedIncludeStep();
  92. for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
  93. Vec3.fromArray(v, position, i);
  94. boundaryHelper.radiusPosition(v);
  95. }
  96. const sphere = boundaryHelper.getSphere();
  97. if (positionCount <= 98) {
  98. const extrema: Vec3[] = [];
  99. for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
  100. extrema.push(Vec3.fromArray(Vec3(), position, i));
  101. }
  102. Sphere3D.setExtrema(sphere, extrema);
  103. }
  104. return sphere;
  105. }
  106. const _mat4 = Mat4();
  107. export function calculateTransformBoundingSphere(invariantBoundingSphere: Sphere3D, transform: Float32Array, transformCount: number): Sphere3D {
  108. if (transformCount === 1) {
  109. Mat4.fromArray(_mat4, transform, 0);
  110. const s = Sphere3D.clone(invariantBoundingSphere);
  111. return Mat4.isIdentity(_mat4) ? s : Sphere3D.transform(s, s, _mat4);
  112. }
  113. const boundaryHelper = getHelper(transformCount);
  114. boundaryHelper.reset();
  115. const { center, radius, extrema } = invariantBoundingSphere;
  116. // only use extrema if there are not too many transforms
  117. if (extrema && transformCount < 50) {
  118. for (let i = 0, _i = transformCount; i < _i; ++i) {
  119. for (const e of extrema) {
  120. Vec3.transformMat4Offset(v, e, transform, 0, 0, i * 16);
  121. boundaryHelper.includePosition(v);
  122. }
  123. }
  124. boundaryHelper.finishedIncludeStep();
  125. for (let i = 0, _i = transformCount; i < _i; ++i) {
  126. for (const e of extrema) {
  127. Vec3.transformMat4Offset(v, e, transform, 0, 0, i * 16);
  128. boundaryHelper.radiusPosition(v);
  129. }
  130. }
  131. } else {
  132. for (let i = 0, _i = transformCount; i < _i; ++i) {
  133. Vec3.transformMat4Offset(v, center, transform, 0, 0, i * 16);
  134. boundaryHelper.includePositionRadius(v, radius);
  135. }
  136. boundaryHelper.finishedIncludeStep();
  137. for (let i = 0, _i = transformCount; i < _i; ++i) {
  138. Vec3.transformMat4Offset(v, center, transform, 0, 0, i * 16);
  139. boundaryHelper.radiusPositionRadius(v, radius);
  140. }
  141. }
  142. return boundaryHelper.getSphere();
  143. }
  144. export function calculateBoundingSphere(position: Float32Array, positionCount: number, transform: Float32Array, transformCount: number, padding = 0, stepFactor = 1): { boundingSphere: Sphere3D, invariantBoundingSphere: Sphere3D } {
  145. const invariantBoundingSphere = calculateInvariantBoundingSphere(position, positionCount, stepFactor);
  146. const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform, transformCount);
  147. Sphere3D.expand(boundingSphere, boundingSphere, padding);
  148. Sphere3D.expand(invariantBoundingSphere, invariantBoundingSphere, padding);
  149. return { boundingSphere, invariantBoundingSphere };
  150. }