<commit_before>/*
Render a pen element.
*/

import { useEffect, useRef } from "react";
import type { Element, Point } from "../types";
import { decompressPath, midPoint } from "../math";

interface Props {
  element: Element;
  renderStatic?: boolean; // if rendering in context of SSR and next.js; forces use of DPI
  // factor = 2 in both frontend and backend, since changing
  // between them breaks hydration badly.
}

// This is enforced by iPad/iOS... but is probably a good idea in general
// to avoid using too much memory and making things slow.
const MAX_CANVAS_SIZE = 4096;

export default function Pen({ element, renderStatic }: Props) {
  const DPIFactor = renderStatic ? 2 : window.devicePixelRatio;
  const canvasRef = useRef<any>(null);
  const scaleRef = useRef<number>(1);
  // We pad to shift things just a little so that parts of the curve that
  // are right on the edge of the canvas don't get partially truncated.
  // I tried doing this at various points in "the pipeline", and here at
  // the renderer is optimal.
  const pad = 2 * (element.data?.["radius"] ?? 1) * scaleRef.current;

  useEffect(() => {
    const canvas = canvasRef.current;
    if (canvas == null) return;
    const ctx = canvas.getContext("2d");
    if (ctx == null) return;
    ctx.restore();
    ctx.save();
    ctx.scale(DPIFactor, DPIFactor);
    ctx.translate(pad, pad);
  }, [pad, element.w, element.h]);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (canvas == null) return;
    const ctx = canvas.getContext("2d");
    if (ctx == null) return;
    const data:
      | { path?: number[]; color?: string; radius?: number; opacity?: number }
      | undefined = element.data;
    if (data == null) return;
    const { path, radius, color, opacity } = data;
    if (path == null) return;
    clearCanvas({ ctx });
    drawCurve({
      ctx,
      path: decompressPath(path, scaleRef.current),
      color: color ?? "black",
      radius: (radius ?? 1) * scaleRef.current,
      opacity,
    });
  }, [element]);
<commit_msg>whiteboard: using two useEffects to accomplish one task was randomly breaking pen drawing<commit_after>/*
Render a pen element.
*/

import { memo, useEffect, useRef } from "react";
import type { Element, Point } from "../types";
import { decompressPath, midPoint } from "../math";

interface Props {
  element: Element;
  renderStatic?: boolean; // if rendering in context of SSR and next.js; forces use of DPI
  // factor = 2 in both frontend and backend, since changing
  // between them breaks hydration badly.
}

// This is enforced by iPad/iOS... but is probably a good idea in general
// to avoid using too much memory and making things slow.
const MAX_CANVAS_SIZE = 4096;

export default function Pen({ element, renderStatic }: Props) {
  const DPIFactor = renderStatic ? 2 : window.devicePixelRatio;
  const canvasRef = useRef<any>(null);
  const scaleRef = useRef<number>(1);
  // We pad to shift things just a little so that parts of the curve that
  // are right on the edge of the canvas don't get partially truncated.
  // I tried doing this at various points in "the pipeline", and here at
  // the renderer is optimal.
  const pad = Math.round(
    2 * (element.data?.["radius"] ?? 1) * scaleRef.current
  );

  useEffect(() => {
    const canvas = canvasRef.current;
    if (canvas == null) return;
    const ctx = canvas.getContext("2d");
    if (ctx == null) return;

    const data:
      | { path?: number[]; color?: string; radius?: number; opacity?: number }
      | undefined = element.data;
    if (data == null) return;

    const { path, radius, color, opacity } = data;
    if (path == null) return;

    ctx.restore();
    ctx.save();
    ctx.scale(DPIFactor, DPIFactor);
    ctx.translate(pad, pad);
    clearCanvas({ ctx });

    drawCurve({
      ctx,
      path: decompressPath(path, scaleRef.current),
      color: color ?? "black",
      radius: (radius ?? 1) * scaleRef.current,
      opacity,
    });
  }, [pad, element]);

{'fraction_non_alphanumeric': 0.12790407194604048, 'fraction_numerical': 0.005745690731951037, 'mean_word_length': 3.119341563786008, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 10, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4334150', 'n_tokens_mistral': 1195, 'n_tokens_neox': 1167, 'n_words': 453}