utils.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import {useLayoutEffect, useState} from 'react';
  2. import {mat3, vec2} from 'gl-matrix';
  3. import type {ViewHierarchyWindow} from 'sentry/components/events/viewHierarchy';
  4. import type {ViewNode} from 'sentry/components/events/viewHierarchy/wireframe';
  5. import {defined} from 'sentry/utils';
  6. import {watchForResize} from 'sentry/utils/profiling/gl/utils';
  7. import {Rect} from 'sentry/utils/profiling/speedscope';
  8. export function useResizeCanvasObserver(canvases: (HTMLCanvasElement | null)[]): Rect {
  9. const [bounds, setCanvasBounds] = useState<Rect>(Rect.Empty());
  10. useLayoutEffect(() => {
  11. if (!canvases.length) {
  12. return undefined;
  13. }
  14. if (canvases.some(c => c === null)) {
  15. return undefined;
  16. }
  17. const observer = watchForResize(canvases as HTMLCanvasElement[], entries => {
  18. const contentRect =
  19. entries[0].contentRect ?? entries[0].target.getBoundingClientRect();
  20. setCanvasBounds(
  21. new Rect(
  22. contentRect.x,
  23. contentRect.y,
  24. contentRect.width * window.devicePixelRatio,
  25. contentRect.height * window.devicePixelRatio
  26. )
  27. );
  28. });
  29. return () => {
  30. observer.disconnect();
  31. };
  32. }, [canvases]);
  33. return bounds;
  34. }
  35. export function getHierarchyDimensions(
  36. hierarchies: ViewHierarchyWindow[],
  37. useAbsolutePosition: boolean = false
  38. ): {
  39. maxHeight: number;
  40. maxWidth: number;
  41. nodes: ViewNode[];
  42. } {
  43. const nodes: ViewNode[] = [];
  44. const queue: [Rect | null, ViewHierarchyWindow][] = [];
  45. for (let i = hierarchies.length - 1; i >= 0; i--) {
  46. queue.push([null, hierarchies[i]]);
  47. }
  48. let maxWidth = Number.MIN_SAFE_INTEGER;
  49. let maxHeight = Number.MIN_SAFE_INTEGER;
  50. while (queue.length) {
  51. const [parent, child] = queue.pop()!;
  52. const node = {
  53. node: child,
  54. rect: new Rect(
  55. useAbsolutePosition ? child.x ?? 0 : (parent?.x ?? 0) + (child.x ?? 0),
  56. useAbsolutePosition ? child.y ?? 0 : (parent?.y ?? 0) + (child.y ?? 0),
  57. child.width ?? 0,
  58. child.height ?? 0
  59. ),
  60. };
  61. nodes.push(node);
  62. if (defined(child.children) && child.children.length) {
  63. // Push the children into the queue in reverse order because the
  64. // output nodes should have early children before later children
  65. // i.e. we need to pop() off early children before ones that come after
  66. for (let i = child.children.length - 1; i >= 0; i--) {
  67. queue.push([node.rect, child.children[i]]);
  68. }
  69. }
  70. maxWidth = Math.max(maxWidth, node.rect.x + (node.rect.width ?? 0));
  71. maxHeight = Math.max(maxHeight, node.rect.y + (node.rect.height ?? 0));
  72. }
  73. return {nodes, maxWidth, maxHeight};
  74. }
  75. export function calculateScale(
  76. bounds: {height: number; width: number},
  77. maxCoordinateDimensions: {height: number; width: number},
  78. border: {x: number; y: number}
  79. ) {
  80. return Math.min(
  81. (bounds.width - border.x) / maxCoordinateDimensions.width,
  82. (bounds.height - border.y) / maxCoordinateDimensions.height
  83. );
  84. }
  85. export function getDeepestNodeAtPoint(
  86. nodes: ViewNode[],
  87. point: vec2,
  88. transformationMatrix: mat3,
  89. scale: number
  90. ): ViewNode | null {
  91. let clickedNode: ViewNode | null = null;
  92. const inverseMatrix = mat3.invert(mat3.create(), transformationMatrix);
  93. if (!inverseMatrix) {
  94. return null;
  95. }
  96. vec2.scale(point, point, scale);
  97. const transformedPoint = vec2.transformMat3(vec2.create(), point, inverseMatrix);
  98. for (let i = 0; i < nodes.length; i++) {
  99. const node = nodes[i];
  100. if (node.rect.contains(transformedPoint)) {
  101. clickedNode = node;
  102. }
  103. }
  104. return clickedNode;
  105. }