gridRenderer.tsx 3.9 KB

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