Загрузка данных


import { CanvasRenderingTarget2D } from 'fancy-canvas';
import { IPrimitivePaneRenderer } from 'lightweight-charts';

import { getThemeStore } from '@src/theme';

import type { Point } from '@core/Drawings/types';

import type { RegressionTrend } from './regressionTrend';

const UI = {
  lineWidth: 2,
  middleLineWidth: 1,

  middleLineDash: 5,
  middleLineGap: 4,

  handleRadius: 5,
  handleBorderWidth: 2,

  correlationFontSize: 11,
  correlationOffsetX: 4,
  correlationOffsetY: 6,
};

export class RegressionTrendPaneRenderer implements IPrimitivePaneRenderer {
  private readonly regressionTrend: RegressionTrend;

  constructor(regressionTrend: RegressionTrend) {
    this.regressionTrend = regressionTrend;
  }

  public draw(target: CanvasRenderingTarget2D): void {
    const data = this.regressionTrend.getRenderData();

    if (!data) {
      return;
    }

    const { colors } = getThemeStore();

    target.useBitmapCoordinateSpace(
      ({
        context,
        horizontalPixelRatio,
        verticalPixelRatio,
      }) => {
        const pixelRatio = Math.max(
          horizontalPixelRatio,
          verticalPixelRatio,
        );

        const baseStart = scalePoint(
          data.baseStartPoint,
          horizontalPixelRatio,
          verticalPixelRatio,
        );

        const baseEnd = scalePoint(
          data.baseEndPoint,
          horizontalPixelRatio,
          verticalPixelRatio,
        );

        const upperStart = scalePoint(
          data.upperStartPoint,
          horizontalPixelRatio,
          verticalPixelRatio,
        );

        const upperEnd = scalePoint(
          data.upperEndPoint,
          horizontalPixelRatio,
          verticalPixelRatio,
        );

        const lowerStart = scalePoint(
          data.lowerStartPoint,
          horizontalPixelRatio,
          verticalPixelRatio,
        );

        const lowerEnd = scalePoint(
          data.lowerEndPoint,
          horizontalPixelRatio,
          verticalPixelRatio,
        );

        context.save();

        if (data.showChannel) {
          drawArea(
            context,
            upperStart,
            upperEnd,
            baseEnd,
            baseStart,
            data.upperFillColor,
          );

          drawArea(
            context,
            baseStart,
            baseEnd,
            lowerEnd,
            lowerStart,
            data.lowerFillColor,
          );

          context.strokeStyle = data.lineColor;
          context.lineWidth = UI.lineWidth * pixelRatio;

          drawLine(
            context,
            upperStart,
            upperEnd,
          );

          drawLine(
            context,
            lowerStart,
            lowerEnd,
          );
        }

        if (
          data.showMiddleLine ||
          !data.showChannel
        ) {
          context.save();

          context.strokeStyle = data.middleLineColor;
          context.lineWidth = UI.middleLineWidth * pixelRatio;

          context.setLineDash([
            UI.middleLineDash * pixelRatio,
            UI.middleLineGap * pixelRatio,
          ]);

          drawLine(
            context,
            baseStart,
            baseEnd,
          );

          context.restore();
        }

        if (
          data.showChannel &&
          data.showCorrelation
        ) {
          drawCorrelation(
            context,
            data.correlation,
            upperStart,
            data.lineColor,
            pixelRatio,
          );
        }

        if (data.showHandles) {
          context.fillStyle = colors.chartBackground;
          context.strokeStyle = colors.chartLineColor;
          context.lineWidth = UI.handleBorderWidth * pixelRatio;

          const radius = UI.handleRadius * pixelRatio;

          drawHandle(
            context,
            baseStart,
            radius,
          );

          drawHandle(
            context,
            baseEnd,
            radius,
          );
        }

        context.restore();
      },
    );
  }
}

function scalePoint(
  point: Point,
  horizontalPixelRatio: number,
  verticalPixelRatio: number,
): Point {
  return {
    x: point.x * horizontalPixelRatio,
    y: point.y * verticalPixelRatio,
  };
}

function drawLine(
  context: CanvasRenderingContext2D,
  start: Point,
  end: Point,
): void {
  context.beginPath();
  context.moveTo(start.x, start.y);
  context.lineTo(end.x, end.y);
  context.stroke();
}

function drawArea(
  context: CanvasRenderingContext2D,
  first: Point,
  second: Point,
  third: Point,
  fourth: Point,
  color: string,
): void {
  context.fillStyle = color;

  context.beginPath();
  context.moveTo(first.x, first.y);
  context.lineTo(second.x, second.y);
  context.lineTo(third.x, third.y);
  context.lineTo(fourth.x, fourth.y);
  context.closePath();

  context.fill();
}

function drawHandle(
  context: CanvasRenderingContext2D,
  point: Point,
  radius: number,
): void {
  context.beginPath();

  context.arc(
    point.x,
    point.y,
    radius,
    0,
    Math.PI * 2,
  );

  context.fill();
  context.stroke();
}

function drawCorrelation(
  context: CanvasRenderingContext2D,
  correlation: number,
  point: Point,
  color: string,
  pixelRatio: number,
): void {
  const fontSize =
    UI.correlationFontSize *
    pixelRatio;

  context.save();

  context.font =
    `${fontSize}px Inter, sans-serif`;

  context.fillStyle = color;
  context.textAlign = 'left';
  context.textBaseline = 'top';

  context.fillText(
    correlation.toFixed(6),
    point.x +
      UI.correlationOffsetX *
        pixelRatio,
    point.y +
      UI.correlationOffsetY *
        pixelRatio,
  );

  context.restore();
}