Skip to content

Commit 3b97f5a

Browse files
authored
feat: move visualdebug to utils and introduce volume bindable visualization (excalidraw#10617)
* feat: Move visualdebug to utils and introduce volume bindable volume visualization Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Move visualdebug to elements Due to dep circles Signed-off-by: Mark Tolmacs <mark@lazycat.hu> --------- Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
1 parent da59205 commit 3b97f5a

4 files changed

Lines changed: 147 additions & 6 deletions

File tree

excalidraw-app/components/DebugCanvas.tsx

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ import { isCurve } from "@excalidraw/math/curve";
2727
import React from "react";
2828

2929
import type { Curve } from "@excalidraw/math";
30-
import type { DebugElement } from "@excalidraw/common";
30+
import type {
31+
DebugElement,
32+
DebugPolygon,
33+
} from "@excalidraw/element/visualdebug";
3134
import type {
3235
ElementsMap,
3336
ExcalidrawArrowElement,
@@ -75,6 +78,44 @@ const renderCubicBezier = (
7578
context.restore();
7679
};
7780

81+
const renderPolygon = (
82+
context: CanvasRenderingContext2D,
83+
zoom: number,
84+
polygon: DebugPolygon,
85+
color: string,
86+
) => {
87+
const { points, fill, close } = polygon;
88+
89+
if (points.length < 2) {
90+
return;
91+
}
92+
93+
context.save();
94+
context.beginPath();
95+
context.moveTo(points[0][0] * zoom, points[0][1] * zoom);
96+
for (let i = 1; i < points.length; i += 1) {
97+
context.lineTo(points[i][0] * zoom, points[i][1] * zoom);
98+
}
99+
if (close !== false) {
100+
context.closePath();
101+
}
102+
103+
if (fill) {
104+
context.save();
105+
context.globalAlpha = 0.15;
106+
context.fillStyle = color;
107+
context.fill();
108+
context.restore();
109+
}
110+
111+
context.strokeStyle = color;
112+
context.stroke();
113+
context.restore();
114+
};
115+
116+
const isDebugPolygon = (data: DebugElement["data"]): data is DebugPolygon =>
117+
(data as DebugPolygon).type === "polygon";
118+
78119
const renderOrigin = (context: CanvasRenderingContext2D, zoom: number) => {
79120
context.strokeStyle = "#888";
80121
context.save();
@@ -280,6 +321,9 @@ const render = (
280321
el.color,
281322
);
282323
break;
324+
case isDebugPolygon(el.data):
325+
renderPolygon(context, appState.zoom.value, el.data, el.color);
326+
break;
283327
default:
284328
throw new Error(`Unknown element type ${JSON.stringify(el)}`);
285329
}

packages/common/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,4 @@ export * from "./random";
1111
export * from "./url";
1212
export * from "./utils";
1313
export * from "./emitter";
14-
export * from "./visualdebug";
1514
export * from "./editorInterface";

packages/element/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
"development": "./dist/dev/index.js",
1818
"production": "./dist/prod/index.js",
1919
"default": "./dist/prod/index.js"
20+
},
21+
"./visualdebug": {
22+
"types": "./dist/types/element/src/visualdebug.d.ts",
23+
"development": "./dist/dev/visualdebug.js",
24+
"production": "./dist/prod/visualdebug.js",
25+
"default": "./dist/prod/visualdebug.js"
2026
}
2127
},
2228
"files": [
Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
import {
22
isLineSegment,
33
lineSegment,
4+
pointDistanceSq,
45
pointFrom,
56
type GlobalPoint,
67
type LocalPoint,
78
} from "@excalidraw/math";
9+
import { type Bounds, isBounds } from "@excalidraw/common";
10+
import {
11+
getElementBounds,
12+
intersectElementWithLineSegment,
13+
isFreeDrawElement,
14+
isLinearElement,
15+
isPathALoop,
16+
} from "@excalidraw/element";
817

18+
import type { ElementsMap, ExcalidrawElement } from "@excalidraw/element/types";
919
import type { Curve } from "@excalidraw/math";
1020
import type { LineSegment } from "@excalidraw/utils";
1121

12-
import { type Bounds, isBounds } from "./bounds";
13-
1422
// The global data holder to collect the debug operations
1523
declare global {
1624
interface Window {
@@ -23,10 +31,69 @@ declare global {
2331

2432
export type DebugElement = {
2533
color: string;
26-
data: LineSegment<GlobalPoint> | Curve<GlobalPoint>;
34+
data: LineSegment<GlobalPoint> | Curve<GlobalPoint> | DebugPolygon;
2735
permanent: boolean;
2836
};
2937

38+
export type DebugPolygon = {
39+
type: "polygon";
40+
points: GlobalPoint[];
41+
fill?: boolean;
42+
close?: boolean;
43+
};
44+
45+
export const debugDrawHitVolume = (
46+
element: ExcalidrawElement,
47+
elementsMap: ElementsMap,
48+
options?: {
49+
rays?: number;
50+
color?: string;
51+
fill?: boolean;
52+
},
53+
) => {
54+
if (
55+
(isLinearElement(element) || isFreeDrawElement(element)) &&
56+
!isPathALoop(element.points)
57+
) {
58+
return;
59+
}
60+
61+
const [x1, y1, x2, y2] = getElementBounds(element, elementsMap);
62+
const center = pointFrom<GlobalPoint>((x1 + x2) / 2, (y1 + y2) / 2);
63+
const rays = options?.rays ?? 100;
64+
const radius = Math.max(x2 - x1, y2 - y1) * 2;
65+
const points: GlobalPoint[] = [];
66+
67+
for (let i = 0; i < rays; i += 1) {
68+
const angle = (i / rays) * Math.PI * 2;
69+
const end = pointFrom<GlobalPoint>(
70+
center[0] + Math.cos(angle) * radius,
71+
center[1] + Math.sin(angle) * radius,
72+
);
73+
const hits = intersectElementWithLineSegment(
74+
element,
75+
elementsMap,
76+
lineSegment(center, end),
77+
);
78+
if (hits.length === 0) {
79+
continue;
80+
}
81+
hits.sort(pointDistanceSq);
82+
points.push(hits[0]);
83+
}
84+
85+
if (points.length >= 3) {
86+
debugDrawPolygon(points, {
87+
color: options?.color ?? "orange",
88+
fill: options?.fill ?? true,
89+
});
90+
} else {
91+
console.warn(
92+
`debugDrawHitVolume: could not compute hit volume for element ${element.id}`,
93+
);
94+
}
95+
};
96+
3097
export const debugDrawCubicBezier = (
3198
c: Curve<GlobalPoint>,
3299
opts?: {
@@ -61,6 +128,31 @@ export const debugDrawLine = (
61128
);
62129
};
63130

131+
export const debugDrawPolygon = (
132+
points: GlobalPoint[],
133+
opts?: {
134+
color?: string;
135+
permanent?: boolean;
136+
fill?: boolean;
137+
close?: boolean;
138+
},
139+
) => {
140+
if (points.length < 2) {
141+
return;
142+
}
143+
144+
addToCurrentFrame({
145+
color: opts?.color ?? "orange",
146+
permanent: !!opts?.permanent,
147+
data: {
148+
type: "polygon",
149+
points,
150+
fill: opts?.fill,
151+
close: opts?.close,
152+
},
153+
});
154+
};
155+
64156
export const debugDrawPoint = (
65157
p: GlobalPoint,
66158
opts?: {
@@ -101,7 +193,7 @@ export const debugDrawBounds = (
101193
permanent?: boolean;
102194
},
103195
) => {
104-
(isBounds(box) ? [box] : box).forEach((bbox) =>
196+
(isBounds(box) ? [box] : box).forEach((bbox: Bounds) =>
105197
debugDrawLine(
106198
[
107199
lineSegment(

0 commit comments

Comments
 (0)