traceView.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import {mat3} from 'gl-matrix';
  2. import clamp from 'sentry/utils/number/clamp';
  3. // Computes the transformation matrix that is used to render scaled
  4. // elements to the DOM and draw the view.
  5. export class TraceView {
  6. // Represents the space of the entire trace, for example
  7. // a trace starting at 0 and ending at 1000 would have a space of [0, 1000]
  8. to_origin: number = 0;
  9. trace_space: DOMView = DOMView.Empty();
  10. // The view defines what the user is currently looking at, it is a subset
  11. // of the trace space. For example, if the user is currently looking at the
  12. // trace from 500 to 1000, the view would be represented by [x, width] = [500, 500]
  13. trace_view: DOMView = DOMView.Empty();
  14. // Represents the pixel space of the entire trace - this is the container
  15. // that we render to. For example, if the container is 1000px wide, the
  16. // pixel space would be [0, 1000]
  17. trace_physical_space: DOMView = DOMView.Empty();
  18. // the encapsulating container that the entire view is rendered to
  19. trace_container_physical_space: DOMView = DOMView.Empty();
  20. public readonly MAX_ZOOM_PRECISION_MS = 1;
  21. setTracePhysicalSpace(
  22. container_space: [x: number, y: number, width: number, height: number],
  23. space: [x: number, y: number, width: number, height: number]
  24. ) {
  25. this.trace_container_physical_space = new DOMView(
  26. 0,
  27. 0,
  28. container_space[2],
  29. container_space[3]
  30. );
  31. this.trace_physical_space = new DOMView(0, 0, space[2], space[3]);
  32. }
  33. setTraceSpace(space: [x: number, y: number, width: number, height: number]) {
  34. this.to_origin = space[0];
  35. this.trace_space = new DOMView(0, 0, space[2], space[3]);
  36. this.trace_view = new DOMView(0, 0, space[2], space[3]);
  37. }
  38. setTraceView(view: {width?: number; x?: number}) {
  39. // In cases where a trace might have a single error, there is no concept of a timeline
  40. if (this.trace_view.width === 0) {
  41. return;
  42. }
  43. const x = view.x ?? this.trace_view.x;
  44. const width = view.width ?? this.trace_view.width;
  45. this.trace_view.x = clamp(
  46. x,
  47. 0,
  48. Math.max(this.trace_space.width - width, this.MAX_ZOOM_PRECISION_MS)
  49. );
  50. this.trace_view.width = clamp(
  51. width,
  52. this.MAX_ZOOM_PRECISION_MS,
  53. this.trace_space.width - this.trace_view.x
  54. );
  55. }
  56. getSpanToPxForSpace(space: [number, number]): mat3 {
  57. const view = new DOMView(space[0], 0, space[1], 0);
  58. const traceViewToSpace = this.trace_space.between(view);
  59. const tracePhysicalToView = this.trace_physical_space.between(this.trace_space);
  60. return mat3.multiply(mat3.create(), traceViewToSpace, tracePhysicalToView);
  61. }
  62. getConfigSpaceCursor(cursor: {x: number; y: number}): [number, number] {
  63. const left_percentage = cursor.x / this.trace_physical_space.width;
  64. const left_view = left_percentage * this.trace_view.width;
  65. return [this.trace_view.x + left_view, 0];
  66. }
  67. }
  68. /**
  69. * Helper class that handles computing transformations between different views to and from DOM space
  70. */
  71. class DOMView {
  72. public x: number;
  73. public y: number;
  74. public width: number;
  75. public height: number;
  76. constructor(x: number, y: number, width: number, height: number) {
  77. this.x = x;
  78. this.y = y;
  79. this.width = width;
  80. this.height = height;
  81. }
  82. static From(view: DOMView): DOMView {
  83. return new DOMView(view.x, view.y, view.width, view.height);
  84. }
  85. static Empty(): DOMView {
  86. return new DOMView(0, 0, 1000, 1);
  87. }
  88. serialize() {
  89. return [this.x, this.y, this.width, this.height];
  90. }
  91. between(to: DOMView): mat3 {
  92. return mat3.fromValues(
  93. to.width / this.width,
  94. 0,
  95. 0,
  96. to.height / this.height,
  97. 0,
  98. 0,
  99. to.x - this.x * (to.width / this.width),
  100. to.y - this.y * (to.height / this.height),
  101. 1
  102. );
  103. }
  104. transform(mat: mat3): [number, number, number, number] {
  105. const x = this.x * mat[0] + this.y * mat[3] + mat[6];
  106. const y = this.x * mat[1] + this.y * mat[4] + mat[7];
  107. const width = this.width * mat[0] + this.height * mat[3];
  108. const height = this.width * mat[1] + this.height * mat[4];
  109. return [x, y, width, height];
  110. }
  111. get center() {
  112. return this.x + this.width / 2;
  113. }
  114. get left() {
  115. return this.x;
  116. }
  117. get right() {
  118. return this.x + this.width;
  119. }
  120. get top() {
  121. return this.y;
  122. }
  123. get bottom() {
  124. return this.y + this.height;
  125. }
  126. }