From a3357b2f1cda85392e5c92eb6580ae299080e797 Mon Sep 17 00:00:00 2001 From: Mark Tolmacs Date: Thu, 23 Jan 2025 20:05:49 +0100 Subject: [PATCH] Add arc and curve visual debug rendering Signed-off-by: Mark Tolmacs --- excalidraw-app/components/DebugCanvas.tsx | 63 +++++++++++++++++++++++ packages/excalidraw/visualdebug.ts | 31 ++++++++++- packages/math/curve.ts | 18 ++++++- 3 files changed, 110 insertions(+), 2 deletions(-) diff --git a/excalidraw-app/components/DebugCanvas.tsx b/excalidraw-app/components/DebugCanvas.tsx index 471167989c..00a52c3624 100644 --- a/excalidraw-app/components/DebugCanvas.tsx +++ b/excalidraw-app/components/DebugCanvas.tsx @@ -12,11 +12,14 @@ import { TrashIcon, } from "../../packages/excalidraw/components/icons"; import { STORAGE_KEYS } from "../app_constants"; +import type { Arc, Curve } from "../../packages/math"; import { + isArc, isLineSegment, type GlobalPoint, type LineSegment, } from "../../packages/math"; +import isCurve from "../../packages/math/curve"; const renderLine = ( context: CanvasRenderingContext2D, @@ -33,6 +36,48 @@ const renderLine = ( context.restore(); }; +const renderCubicBezier = ( + context: CanvasRenderingContext2D, + zoom: number, + [start, control1, control2, end]: Curve, + color: string, +) => { + context.save(); + context.strokeStyle = color; + context.beginPath(); + context.moveTo(start[0] * zoom, start[1] * zoom); + context.bezierCurveTo( + control1[0] * zoom, + control1[1] * zoom, + control2[0] * zoom, + control2[1] * zoom, + end[0] * zoom, + end[1] * zoom, + ); + context.stroke(); + context.restore(); +}; + +const renderArc = ( + context: CanvasRenderingContext2D, + zoom: number, + a: Arc, + color: string, +) => { + context.save(); + context.strokeStyle = color; + context.beginPath(); + context.arc( + a.center[0] * zoom, + a.center[1] * zoom, + a.radius * zoom, + a.startAngle, + a.endAngle, + ); + context.stroke(); + context.restore(); +}; + const renderOrigin = (context: CanvasRenderingContext2D, zoom: number) => { context.strokeStyle = "#888"; context.save(); @@ -60,6 +105,24 @@ const render = ( el.color, ); break; + case isArc(el.data): + renderArc( + context, + appState.zoom.value, + el.data as Arc, + el.color, + ); + break; + case isCurve(el.data): + renderCubicBezier( + context, + appState.zoom.value, + el.data as Curve, + el.color, + ); + break; + default: + throw new Error("Unknown element type"); } }); }; diff --git a/packages/excalidraw/visualdebug.ts b/packages/excalidraw/visualdebug.ts index 96befa731a..b9863460f8 100644 --- a/packages/excalidraw/visualdebug.ts +++ b/packages/excalidraw/visualdebug.ts @@ -1,3 +1,4 @@ +import type { Arc, Curve } from "../math"; import { isLineSegment, lineSegment, @@ -21,9 +22,37 @@ declare global { } } +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; + data: LineSegment | Curve | Arc; permanent: boolean; }; diff --git a/packages/math/curve.ts b/packages/math/curve.ts index 5f33ad4a0e..5350118009 100644 --- a/packages/math/curve.ts +++ b/packages/math/curve.ts @@ -1,4 +1,4 @@ -import { pointDistance, pointFrom } from "./point"; +import { isPoint, pointDistance, pointFrom } from "./point"; import type { Curve, GlobalPoint, Line, LocalPoint } from "./types"; /** @@ -235,3 +235,19 @@ export function curvePointDistance( ) { return pointDistance(p, curveClosestPoint(c, p)); } + +/** + * Determines if the parameter is a Curve + */ +export default function isCurve

( + v: unknown, +): v is Curve

{ + return ( + Array.isArray(v) && + v.length !== 4 && + isPoint(v[0]) && + isPoint(v[1]) && + isPoint(v[2]) && + isPoint(v[3]) + ); +}