public renderLine()

in src/core/prototypes/plot_segments/axis.ts [499:927]


  public renderLine(
    x: number,
    y: number,
    angle: number,
    side: number,
    offset?: number
  ): Group {
    const g = makeGroup([]);
    const style = this.style;
    const rangeMin = this.rangeMin;
    const rangeMax = this.rangeMax;
    const tickSize = style.tickSize;
    const lineStyle: Style = {
      strokeLinecap: "square",
      strokeColor: style.lineColor,
    };
    AxisRenderer.textMeasurer.setFontFamily(style.fontFamily);
    AxisRenderer.textMeasurer.setFontSize(style.fontSize);
    if (this.oppositeSide) {
      side = -side;
    }

    //shift axis for scrollbar space
    if (this.scrollRequired && this.shiftAxis) {
      if (angle === 90) {
        x += side * AxisRenderer.SCROLL_BAR_SIZE;
      }
      if (angle === 0) {
        y += -side * AxisRenderer.SCROLL_BAR_SIZE;
      }
    }

    const cos = Math.cos(Geometry.degreesToRadians(angle));
    const sin = Math.sin(Geometry.degreesToRadians(angle));
    const x1 = x + rangeMin * cos;
    const y1 = y + rangeMin * sin;
    const x2 = x + rangeMax * cos;
    const y2 = y + rangeMax * sin;

    // Base line
    if (style.showBaseline) {
      g.elements.push(makeLine(x1, y1, x2, y2, lineStyle));
    }
    // Ticks
    const ticksData = this.ticks.map((x) => x.position);
    const visibleTicks = ticksData.concat([rangeMin, rangeMax]);

    if (style.showTicks) {
      for (const tickPosition of visibleTicks) {
        const tx = x + tickPosition * cos;
        const ty = y + tickPosition * sin;
        const dx = side * tickSize * sin;
        const dy = -side * tickSize * cos;
        g.elements.push(makeLine(tx, ty, tx + dx, ty + dy, lineStyle));
      }
    }
    // Tick texts
    const ticks = this.ticks.map((x) => {
      return {
        position: x.position,
        label: x.label,
        measure: AxisRenderer.textMeasurer.measure(x.label),
      };
    });
    let maxTextWidth = 0;
    let maxTickDistance = 0;
    for (let i = 0; i < ticks.length; i++) {
      maxTextWidth = Math.max(maxTextWidth, ticks[i].measure.width);
      if (i > 0) {
        maxTickDistance = Math.max(
          maxTickDistance,
          Math.abs(ticks[i - 1].position - ticks[i].position)
        );
      }
    }
    for (const tick of ticks) {
      const tx = x + tick.position * cos,
        ty = y + tick.position * sin;
      const offset = 3;
      const dx = side * (tickSize + offset) * sin,
        dy = -side * (tickSize + offset) * cos;

      if (Math.abs(cos) < 0.5) {
        if (
          style.wordWrap ||
          (typeof tick.label === "string" &&
            splitStringByNewLine(tick.label).length > 1)
        ) {
          let textContent: string[] = splitByWidth(
            replaceSymbolByTab(replaceSymbolByNewLine(tick.label)),
            maxTickDistance,
            10000,
            style.fontFamily,
            style.fontSize
          );
          textContent = textContent.flatMap((line) =>
            splitStringByNewLine(line)
          );
          const lines: Graphics.Element[] = [];
          for (let index = 0; index < textContent.length; index++) {
            const [px, py] = TextMeasurer.ComputeTextPosition(
              0,
              0,
              AxisRenderer.textMeasurer.measure(textContent[index]),
              style.verticalText ? "middle" : side * sin < 0 ? "right" : "left",
              style.verticalText
                ? side * sin < 0
                  ? "bottom"
                  : "top"
                : "middle",
              0
            );
            const text = makeText(
              px,
              py -
                style.fontSize * index +
                (side * cos > 0
                  ? 0
                  : (textContent.length * style.fontSize - style.fontSize) / 2),
              textContent[index],
              style.fontFamily,
              style.fontSize,
              {
                fillColor: style.tickColor,
                backgroundColor: style.tickTextBackgroudColor,
                backgroundColorId: style.tickTextBackgroudColorId,
              }
            );
            lines.push(text);
          }
          const gText = makeGroup(lines);
          gText.transform = {
            x: tx + dx,
            y: ty + dy,
            angle: style.verticalText ? angle : 0,
          };
          g.elements.push(gText);
        } else {
          // 60 ~ 120 degree
          const [px, py] = TextMeasurer.ComputeTextPosition(
            0,
            0,
            tick.measure,
            side * sin < 0 ? "right" : "left",
            "middle",
            0
          );
          const gText = makeGroup([
            makeText(
              px,
              py,
              tick.label,
              style.fontFamily,
              style.fontSize,
              {
                fillColor: style.tickColor,
                backgroundColor: style.tickTextBackgroudColor,
                backgroundColorId: style.tickTextBackgroudColorId,
              },
              this.plotSegment && this.dataFlow
                ? {
                    enableSelection: this.data.enableSelection,
                    glyphIndex: 1,
                    rowIndices: applySelectionFilter(
                      this.data,
                      this.plotSegment.table,
                      ticks.indexOf(tick),
                      this.dataFlow
                    ),
                    plotSegment: this.plotSegment,
                  }
                : undefined
            ),
          ]);
          gText.transform = {
            x: tx + dx,
            y: ty + dy,
            angle: style.verticalText ? (sin > 0 ? angle - 90 : angle + 90) : 0,
          };
          g.elements.push(gText);
        }
      } else if (Math.abs(cos) < Math.sqrt(3) / 2) {
        const [px, py] = TextMeasurer.ComputeTextPosition(
          0,
          0,
          tick.measure,
          side * sin < 0 ? "right" : "left",
          "middle",
          0
        );
        const gText = makeGroup([
          makeText(
            px,
            py,
            tick.label,
            style.fontFamily,
            style.fontSize,
            {
              fillColor: style.tickColor,
              backgroundColor: style.tickTextBackgroudColor,
              backgroundColorId: style.tickTextBackgroudColorId,
            },
            this.plotSegment && this.dataFlow
              ? {
                  enableSelection: this.data.enableSelection,
                  glyphIndex: 1,
                  rowIndices: applySelectionFilter(
                    this.data,
                    this.plotSegment.table,
                    ticks.indexOf(tick),
                    this.dataFlow
                  ),
                  plotSegment: this.plotSegment,
                }
              : undefined
          ),
        ]);
        gText.transform = {
          x: tx + dx,
          y: ty + dy,
          angle: style.verticalText ? (sin > 0 ? angle - 90 : angle + 90) : 0,
        };
        g.elements.push(gText);
      } else {
        if (
          !style.wordWrap &&
          maxTextWidth > maxTickDistance &&
          typeof tick.label === "string" &&
          splitStringByNewLine(tick.label).length === 1
        ) {
          const [px, py] = TextMeasurer.ComputeTextPosition(
            0,
            0,
            tick.measure,
            style.verticalText
              ? side * cos > 0
                ? "right"
                : "left"
              : style.wordWrap
              ? "middle"
              : side * cos > 0
              ? "right"
              : "left",
            style.verticalText
              ? "middle"
              : style.wordWrap
              ? "middle"
              : side * cos > 0
              ? "top"
              : "bottom",
            0
          );
          const gText = makeGroup([
            makeText(
              px,
              py,
              tick.label,
              style.fontFamily,
              style.fontSize,
              {
                fillColor: style.tickColor,
                backgroundColor: style.tickTextBackgroudColor,
                backgroundColorId: style.tickTextBackgroudColorId,
              },
              this.plotSegment && this.dataFlow
                ? {
                    enableSelection: this.data.enableSelection,
                    glyphIndex: 1,
                    rowIndices: applySelectionFilter(
                      this.data,
                      this.plotSegment.table,
                      ticks.indexOf(tick),
                      this.dataFlow
                    ),
                    plotSegment: this.plotSegment,
                  }
                : undefined
            ),
          ]);
          gText.transform = {
            x: tx + dx,
            y: ty + dy,
            angle: style.verticalText
              ? cos > 0
                ? 90 + angle
                : 90 + angle - 180
              : cos > 0
              ? 36 + angle
              : 36 + angle - 180,
          };
          g.elements.push(gText);
        } else {
          if (
            style.wordWrap ||
            (typeof tick.label === "string" &&
              splitStringByNewLine(tick.label).length > 1)
          ) {
            let textContent = [
              replaceSymbolByTab(replaceSymbolByNewLine(tick.label)),
            ];
            if (style.wordWrap) {
              textContent = splitByWidth(
                replaceSymbolByTab(replaceSymbolByNewLine(tick.label)),
                maxTickDistance,
                10000,
                style.fontFamily,
                style.fontSize
              );
            }
            textContent = textContent.flatMap((line) =>
              splitStringByNewLine(line)
            );
            const lines: Graphics.Element[] = [];
            for (let index = 0; index < textContent.length; index++) {
              const [px, py] = TextMeasurer.ComputeTextPosition(
                0,
                0,
                AxisRenderer.textMeasurer.measure(textContent[index]),
                style.wordWrap ? "middle" : side * cos > 0 ? "right" : "left",
                side * cos > 0 ? "top" : "bottom",
                0
              );
              const text = makeText(
                px,
                py -
                  style.fontSize * index +
                  (side * cos > 0 ? 0 : textContent.length * style.fontSize),
                textContent[index],
                style.fontFamily,
                style.fontSize,
                {
                  fillColor: style.tickColor,
                  backgroundColor: style.tickTextBackgroudColor,
                  backgroundColorId: style.tickTextBackgroudColorId,
                },
                this.plotSegment && this.dataFlow
                  ? {
                      enableSelection: this.data.enableSelection,
                      glyphIndex: 1,
                      rowIndices: applySelectionFilter(
                        this.data,
                        this.plotSegment.table,
                        ticks.indexOf(tick),
                        this.dataFlow
                      ),
                      plotSegment: this.plotSegment,
                    }
                  : undefined
              );
              lines.push(text);
            }
            const gText = makeGroup(lines);

            gText.transform = {
              x: tx + dx,
              y: ty + dy,
              angle: style.verticalText
                ? style.wordWrap
                  ? 0
                  : cos > 0
                  ? 90 + angle
                  : 90 + angle - 180
                : style.wordWrap
                ? 0
                : cos > 0
                ? 36 + angle
                : 36 + angle - 180,
            };
            g.elements.push(gText);
          } else {
            const [px, py] = TextMeasurer.ComputeTextPosition(
              0,
              0,
              tick.measure,
              style.verticalText
                ? side * cos > 0
                  ? "right"
                  : "left"
                : "middle",
              style.verticalText ? "middle" : side * cos > 0 ? "top" : "bottom",
              0
            );
            const gText = makeGroup([
              makeText(
                px,
                py,
                tick.label,
                style.fontFamily,
                style.fontSize,
                {
                  fillColor: style.tickColor,
                  backgroundColor: style.tickTextBackgroudColor,
                  backgroundColorId: style.tickTextBackgroudColorId,
                },
                this.plotSegment && this.dataFlow
                  ? {
                      enableSelection: this.data.enableSelection,
                      glyphIndex: 1,
                      rowIndices: applySelectionFilter(
                        this.data,
                        this.plotSegment.table,
                        ticks.indexOf(tick),
                        this.dataFlow
                      ),
                      plotSegment: this.plotSegment,
                    }
                  : undefined
              ),
            ]);
            gText.transform = {
              x: tx + dx,
              y: ty + dy,
              angle: style.verticalText ? 90 + angle : 0,
            };
            g.elements.push(gText);
          }
        }
      }
    }

    if (offset) {
      g.transform = {
        x: angle == 90 ? offset : 0,
        y: angle == 90 ? 0 : offset,
        angle: 0,
      };
    }
    return g;
  }