gridRenderer.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import {mat3} from 'gl-matrix';
  2. import {computeInterval, Rect} from 'sentry/utils/profiling/speedscope';
  3. import {FlamegraphTheme} from '../flamegraph/flamegraphTheme';
  4. import {getContext, measureText} from '../gl/utils';
  5. export function getIntervalTimeAtX(logicalSpaceToConfigView: mat3, x: number): number {
  6. const vector = logicalSpaceToConfigView[0] * x + logicalSpaceToConfigView[6];
  7. if (vector > 1) {
  8. return Math.round(vector);
  9. }
  10. return Math.round(vector * 10) / 10;
  11. }
  12. class GridRenderer {
  13. canvas: HTMLCanvasElement;
  14. context: CanvasRenderingContext2D;
  15. theme: FlamegraphTheme;
  16. formatter: (value: number) => string;
  17. constructor(
  18. canvas: HTMLCanvasElement,
  19. theme: FlamegraphTheme,
  20. formatter: (value: number) => string
  21. ) {
  22. this.canvas = canvas;
  23. this.theme = theme;
  24. this.formatter = formatter;
  25. this.context = getContext(canvas, '2d');
  26. }
  27. draw(
  28. configViewSpace: Rect,
  29. physicalViewRect: Rect,
  30. configViewToPhysicalSpace: mat3,
  31. logicalSpaceToConfigView: mat3,
  32. drawGridTicks: boolean = true
  33. ): void {
  34. this.context.font = `${
  35. this.theme.SIZES.LABEL_FONT_SIZE * window.devicePixelRatio
  36. }px ${this.theme.FONTS.FONT}`;
  37. this.context.textBaseline = 'top';
  38. this.context.lineWidth = this.theme.SIZES.GRID_LINE_WIDTH / 2;
  39. // Draw the background of the top timeline
  40. this.context.fillStyle = this.theme.COLORS.GRID_FRAME_BACKGROUND_COLOR;
  41. this.context.fillRect(
  42. 0,
  43. 0,
  44. physicalViewRect.width,
  45. this.theme.SIZES.TIMELINE_HEIGHT * window.devicePixelRatio
  46. );
  47. // Draw top timeline lines
  48. this.context.fillStyle = this.theme.COLORS.GRID_LINE_COLOR;
  49. this.context.fillRect(
  50. 0,
  51. 0,
  52. physicalViewRect.width,
  53. this.theme.SIZES.GRID_LINE_WIDTH / 2
  54. );
  55. this.context.fillRect(
  56. 0,
  57. this.theme.SIZES.TIMELINE_HEIGHT * window.devicePixelRatio,
  58. physicalViewRect.width,
  59. this.theme.SIZES.GRID_LINE_WIDTH / 2
  60. );
  61. if (drawGridTicks) {
  62. const intervals = computeInterval(
  63. configViewSpace,
  64. logicalSpaceToConfigView,
  65. getIntervalTimeAtX
  66. );
  67. for (let i = 0; i < intervals.length; i++) {
  68. // Compute the x position of our interval from config space to physical
  69. const physicalIntervalPosition = Math.round(
  70. intervals[i] * configViewToPhysicalSpace[0] + configViewToPhysicalSpace[6]
  71. );
  72. // Format the label text
  73. const labelText = this.formatter(intervals[i]);
  74. this.context.fillStyle = this.theme.COLORS.LABEL_FONT_COLOR;
  75. // Subtract width of the text and padding so that the text is align to the left of our interval
  76. this.context.fillText(
  77. labelText,
  78. physicalIntervalPosition -
  79. measureText(labelText, this.context).width -
  80. this.theme.SIZES.LABEL_FONT_PADDING * window.devicePixelRatio,
  81. this.theme.SIZES.LABEL_FONT_PADDING * window.devicePixelRatio
  82. );
  83. // Draw the vertical grid line
  84. this.context.strokeStyle = this.theme.COLORS.GRID_LINE_COLOR;
  85. this.context.strokeRect(
  86. physicalIntervalPosition - this.theme.SIZES.GRID_LINE_WIDTH / 2,
  87. physicalViewRect.y,
  88. this.theme.SIZES.GRID_LINE_WIDTH / 2,
  89. physicalViewRect.height
  90. );
  91. }
  92. }
  93. }
  94. }
  95. export {GridRenderer};