import type { Arc, Curve } from "../math"; import { isLineSegment, lineSegment, pointFrom, type GlobalPoint, type LocalPoint, } from "../math"; import type { LineSegment } from "../utils"; import type { BoundingBox, Bounds } from "./element/bounds"; import { isBounds } from "./element/typeChecks"; // The global data holder to collect the debug operations declare global { interface Window { visualDebug?: { data: DebugElement[][]; currentFrame?: number; }; debugDrawPoint: typeof debugDrawPoint; debugDrawLine: typeof debugDrawLine; } } export const debugDrawCubicBezier = ( c: Curve, opts?: { color?: string; permanent?: boolean; }, ) => { addToCurrentFrame({ color: opts?.color ?? "purple", permanent: !!opts?.permanent, data: c, }); }; export const debugDrawArc = ( a: Arc, opts?: { color?: string; permanent?: boolean; }, ) => { addToCurrentFrame({ color: opts?.color ?? "blue", permanent: !!opts?.permanent, data: a, }); }; export type DebugElement = { color: string; data: LineSegment | Curve | Arc; permanent: boolean; }; export const debugDrawLine = ( segment: LineSegment | LineSegment[], opts?: { color?: string; permanent?: boolean; }, ) => { const segments = ( isLineSegment(segment) ? [segment] : segment ) as LineSegment[]; segments.forEach((data) => addToCurrentFrame({ color: opts?.color ?? "red", data, permanent: !!opts?.permanent, }), ); }; export const debugDrawPoint = ( p: GlobalPoint, opts?: { color?: string; permanent?: boolean; fuzzy?: boolean; }, ) => { const xOffset = opts?.fuzzy ? Math.random() * 3 : 0; const yOffset = opts?.fuzzy ? Math.random() * 3 : 0; debugDrawLine( lineSegment( pointFrom(p[0] + xOffset - 10, p[1] + yOffset - 10), pointFrom(p[0] + xOffset + 10, p[1] + yOffset + 10), ), { color: opts?.color ?? "cyan", permanent: opts?.permanent, }, ); debugDrawLine( lineSegment( pointFrom(p[0] + xOffset - 10, p[1] + yOffset + 10), pointFrom(p[0] + xOffset + 10, p[1] + yOffset - 10), ), { color: opts?.color ?? "cyan", permanent: opts?.permanent, }, ); }; export const debugDrawBoundingBox = ( box: BoundingBox | BoundingBox[], opts?: { color?: string; permanent?: boolean; }, ) => { (Array.isArray(box) ? box : [box]).forEach((bbox) => debugDrawLine( [ lineSegment( pointFrom(bbox.minX, bbox.minY), pointFrom(bbox.maxX, bbox.minY), ), lineSegment( pointFrom(bbox.maxX, bbox.minY), pointFrom(bbox.maxX, bbox.maxY), ), lineSegment( pointFrom(bbox.maxX, bbox.maxY), pointFrom(bbox.minX, bbox.maxY), ), lineSegment( pointFrom(bbox.minX, bbox.maxY), pointFrom(bbox.minX, bbox.minY), ), ], { color: opts?.color ?? "cyan", permanent: opts?.permanent, }, ), ); }; export const debugDrawBounds = ( box: Bounds | Bounds[], opts?: { color?: string; permanent?: boolean; }, ) => { (isBounds(box) ? [box] : box).forEach((bbox) => debugDrawLine( [ lineSegment( pointFrom(bbox[0], bbox[1]), pointFrom(bbox[2], bbox[1]), ), lineSegment( pointFrom(bbox[2], bbox[1]), pointFrom(bbox[2], bbox[3]), ), lineSegment( pointFrom(bbox[2], bbox[3]), pointFrom(bbox[0], bbox[3]), ), lineSegment( pointFrom(bbox[0], bbox[3]), pointFrom(bbox[0], bbox[1]), ), ], { color: opts?.color ?? "green", permanent: !!opts?.permanent, }, ), ); }; export const debugDrawPoints = ( { x, y, points, }: { x: number; y: number; points: LocalPoint[]; }, options?: any, ) => { points.forEach((p) => debugDrawPoint(pointFrom(x + p[0], y + p[1]), options), ); }; export const debugCloseFrame = () => { window.visualDebug?.data.push([]); }; export const debugClear = () => { if (window.visualDebug?.data) { window.visualDebug.data = []; } }; const addToCurrentFrame = (element: DebugElement) => { if (window.visualDebug?.data && window.visualDebug.data.length === 0) { window.visualDebug.data[0] = []; } window.visualDebug?.data && window.visualDebug.data[window.visualDebug.data.length - 1].push(element); };