feat: move visualdebug to utils and introduce volume bindable visualization (#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>
This commit is contained in:
@@ -27,7 +27,10 @@ import { isCurve } from "@excalidraw/math/curve";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import type { Curve } from "@excalidraw/math";
|
import type { Curve } from "@excalidraw/math";
|
||||||
import type { DebugElement } from "@excalidraw/common";
|
import type {
|
||||||
|
DebugElement,
|
||||||
|
DebugPolygon,
|
||||||
|
} from "@excalidraw/element/visualdebug";
|
||||||
import type {
|
import type {
|
||||||
ElementsMap,
|
ElementsMap,
|
||||||
ExcalidrawArrowElement,
|
ExcalidrawArrowElement,
|
||||||
@@ -75,6 +78,44 @@ const renderCubicBezier = (
|
|||||||
context.restore();
|
context.restore();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderPolygon = (
|
||||||
|
context: CanvasRenderingContext2D,
|
||||||
|
zoom: number,
|
||||||
|
polygon: DebugPolygon,
|
||||||
|
color: string,
|
||||||
|
) => {
|
||||||
|
const { points, fill, close } = polygon;
|
||||||
|
|
||||||
|
if (points.length < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.save();
|
||||||
|
context.beginPath();
|
||||||
|
context.moveTo(points[0][0] * zoom, points[0][1] * zoom);
|
||||||
|
for (let i = 1; i < points.length; i += 1) {
|
||||||
|
context.lineTo(points[i][0] * zoom, points[i][1] * zoom);
|
||||||
|
}
|
||||||
|
if (close !== false) {
|
||||||
|
context.closePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fill) {
|
||||||
|
context.save();
|
||||||
|
context.globalAlpha = 0.15;
|
||||||
|
context.fillStyle = color;
|
||||||
|
context.fill();
|
||||||
|
context.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
context.strokeStyle = color;
|
||||||
|
context.stroke();
|
||||||
|
context.restore();
|
||||||
|
};
|
||||||
|
|
||||||
|
const isDebugPolygon = (data: DebugElement["data"]): data is DebugPolygon =>
|
||||||
|
(data as DebugPolygon).type === "polygon";
|
||||||
|
|
||||||
const renderOrigin = (context: CanvasRenderingContext2D, zoom: number) => {
|
const renderOrigin = (context: CanvasRenderingContext2D, zoom: number) => {
|
||||||
context.strokeStyle = "#888";
|
context.strokeStyle = "#888";
|
||||||
context.save();
|
context.save();
|
||||||
@@ -280,6 +321,9 @@ const render = (
|
|||||||
el.color,
|
el.color,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case isDebugPolygon(el.data):
|
||||||
|
renderPolygon(context, appState.zoom.value, el.data, el.color);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown element type ${JSON.stringify(el)}`);
|
throw new Error(`Unknown element type ${JSON.stringify(el)}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,5 +11,4 @@ export * from "./random";
|
|||||||
export * from "./url";
|
export * from "./url";
|
||||||
export * from "./utils";
|
export * from "./utils";
|
||||||
export * from "./emitter";
|
export * from "./emitter";
|
||||||
export * from "./visualdebug";
|
|
||||||
export * from "./editorInterface";
|
export * from "./editorInterface";
|
||||||
|
|||||||
@@ -17,6 +17,12 @@
|
|||||||
"development": "./dist/dev/index.js",
|
"development": "./dist/dev/index.js",
|
||||||
"production": "./dist/prod/index.js",
|
"production": "./dist/prod/index.js",
|
||||||
"default": "./dist/prod/index.js"
|
"default": "./dist/prod/index.js"
|
||||||
|
},
|
||||||
|
"./visualdebug": {
|
||||||
|
"types": "./dist/types/element/src/visualdebug.d.ts",
|
||||||
|
"development": "./dist/dev/visualdebug.js",
|
||||||
|
"production": "./dist/prod/visualdebug.js",
|
||||||
|
"default": "./dist/prod/visualdebug.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
|||||||
@@ -1,16 +1,24 @@
|
|||||||
import {
|
import {
|
||||||
isLineSegment,
|
isLineSegment,
|
||||||
lineSegment,
|
lineSegment,
|
||||||
|
pointDistanceSq,
|
||||||
pointFrom,
|
pointFrom,
|
||||||
type GlobalPoint,
|
type GlobalPoint,
|
||||||
type LocalPoint,
|
type LocalPoint,
|
||||||
} from "@excalidraw/math";
|
} from "@excalidraw/math";
|
||||||
|
import { type Bounds, isBounds } from "@excalidraw/common";
|
||||||
|
import {
|
||||||
|
getElementBounds,
|
||||||
|
intersectElementWithLineSegment,
|
||||||
|
isFreeDrawElement,
|
||||||
|
isLinearElement,
|
||||||
|
isPathALoop,
|
||||||
|
} from "@excalidraw/element";
|
||||||
|
|
||||||
|
import type { ElementsMap, ExcalidrawElement } from "@excalidraw/element/types";
|
||||||
import type { Curve } from "@excalidraw/math";
|
import type { Curve } from "@excalidraw/math";
|
||||||
import type { LineSegment } from "@excalidraw/utils";
|
import type { LineSegment } from "@excalidraw/utils";
|
||||||
|
|
||||||
import { type Bounds, isBounds } from "./bounds";
|
|
||||||
|
|
||||||
// The global data holder to collect the debug operations
|
// The global data holder to collect the debug operations
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@@ -23,10 +31,69 @@ declare global {
|
|||||||
|
|
||||||
export type DebugElement = {
|
export type DebugElement = {
|
||||||
color: string;
|
color: string;
|
||||||
data: LineSegment<GlobalPoint> | Curve<GlobalPoint>;
|
data: LineSegment<GlobalPoint> | Curve<GlobalPoint> | DebugPolygon;
|
||||||
permanent: boolean;
|
permanent: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DebugPolygon = {
|
||||||
|
type: "polygon";
|
||||||
|
points: GlobalPoint[];
|
||||||
|
fill?: boolean;
|
||||||
|
close?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const debugDrawHitVolume = (
|
||||||
|
element: ExcalidrawElement,
|
||||||
|
elementsMap: ElementsMap,
|
||||||
|
options?: {
|
||||||
|
rays?: number;
|
||||||
|
color?: string;
|
||||||
|
fill?: boolean;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
if (
|
||||||
|
(isLinearElement(element) || isFreeDrawElement(element)) &&
|
||||||
|
!isPathALoop(element.points)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [x1, y1, x2, y2] = getElementBounds(element, elementsMap);
|
||||||
|
const center = pointFrom<GlobalPoint>((x1 + x2) / 2, (y1 + y2) / 2);
|
||||||
|
const rays = options?.rays ?? 100;
|
||||||
|
const radius = Math.max(x2 - x1, y2 - y1) * 2;
|
||||||
|
const points: GlobalPoint[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < rays; i += 1) {
|
||||||
|
const angle = (i / rays) * Math.PI * 2;
|
||||||
|
const end = pointFrom<GlobalPoint>(
|
||||||
|
center[0] + Math.cos(angle) * radius,
|
||||||
|
center[1] + Math.sin(angle) * radius,
|
||||||
|
);
|
||||||
|
const hits = intersectElementWithLineSegment(
|
||||||
|
element,
|
||||||
|
elementsMap,
|
||||||
|
lineSegment(center, end),
|
||||||
|
);
|
||||||
|
if (hits.length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
hits.sort(pointDistanceSq);
|
||||||
|
points.push(hits[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (points.length >= 3) {
|
||||||
|
debugDrawPolygon(points, {
|
||||||
|
color: options?.color ?? "orange",
|
||||||
|
fill: options?.fill ?? true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`debugDrawHitVolume: could not compute hit volume for element ${element.id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const debugDrawCubicBezier = (
|
export const debugDrawCubicBezier = (
|
||||||
c: Curve<GlobalPoint>,
|
c: Curve<GlobalPoint>,
|
||||||
opts?: {
|
opts?: {
|
||||||
@@ -61,6 +128,31 @@ export const debugDrawLine = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const debugDrawPolygon = (
|
||||||
|
points: GlobalPoint[],
|
||||||
|
opts?: {
|
||||||
|
color?: string;
|
||||||
|
permanent?: boolean;
|
||||||
|
fill?: boolean;
|
||||||
|
close?: boolean;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
if (points.length < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addToCurrentFrame({
|
||||||
|
color: opts?.color ?? "orange",
|
||||||
|
permanent: !!opts?.permanent,
|
||||||
|
data: {
|
||||||
|
type: "polygon",
|
||||||
|
points,
|
||||||
|
fill: opts?.fill,
|
||||||
|
close: opts?.close,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const debugDrawPoint = (
|
export const debugDrawPoint = (
|
||||||
p: GlobalPoint,
|
p: GlobalPoint,
|
||||||
opts?: {
|
opts?: {
|
||||||
@@ -101,7 +193,7 @@ export const debugDrawBounds = (
|
|||||||
permanent?: boolean;
|
permanent?: boolean;
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
(isBounds(box) ? [box] : box).forEach((bbox) =>
|
(isBounds(box) ? [box] : box).forEach((bbox: Bounds) =>
|
||||||
debugDrawLine(
|
debugDrawLine(
|
||||||
[
|
[
|
||||||
lineSegment(
|
lineSegment(
|
||||||
Reference in New Issue
Block a user