Merge branch 'master' into arnost/scroll-in-read-only-links
# Conflicts: # packages/excalidraw/components/App.tsx
This commit is contained in:
@@ -5,7 +5,7 @@ import {
|
||||
type LocalPoint,
|
||||
} from "@excalidraw/math";
|
||||
|
||||
import type { Bounds } from "@excalidraw/element/bounds";
|
||||
import type { Bounds } from "@excalidraw/element";
|
||||
|
||||
export type LineSegment<P extends LocalPoint | GlobalPoint> = [P, P];
|
||||
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
import {
|
||||
lineSegment,
|
||||
pointFrom,
|
||||
polygonIncludesPoint,
|
||||
pointOnLineSegment,
|
||||
pointOnPolygon,
|
||||
polygonFromPoints,
|
||||
type GlobalPoint,
|
||||
type LocalPoint,
|
||||
type Polygon,
|
||||
} from "@excalidraw/math";
|
||||
|
||||
import type { Curve } from "@excalidraw/math";
|
||||
|
||||
import { pointInEllipse, pointOnEllipse } from "./shape";
|
||||
|
||||
import type { Polycurve, Polyline, GeometricShape } from "./shape";
|
||||
|
||||
// check if the given point is considered on the given shape's border
|
||||
export const isPointOnShape = <Point extends GlobalPoint | LocalPoint>(
|
||||
point: Point,
|
||||
shape: GeometricShape<Point>,
|
||||
tolerance = 0,
|
||||
) => {
|
||||
// get the distance from the given point to the given element
|
||||
// check if the distance is within the given epsilon range
|
||||
switch (shape.type) {
|
||||
case "polygon":
|
||||
return pointOnPolygon(point, shape.data, tolerance);
|
||||
case "ellipse":
|
||||
return pointOnEllipse(point, shape.data, tolerance);
|
||||
case "line":
|
||||
return pointOnLineSegment(point, shape.data, tolerance);
|
||||
case "polyline":
|
||||
return pointOnPolyline(point, shape.data, tolerance);
|
||||
case "curve":
|
||||
return pointOnCurve(point, shape.data, tolerance);
|
||||
case "polycurve":
|
||||
return pointOnPolycurve(point, shape.data, tolerance);
|
||||
default:
|
||||
throw Error(`shape ${shape} is not implemented`);
|
||||
}
|
||||
};
|
||||
|
||||
// check if the given point is considered inside the element's border
|
||||
export const isPointInShape = <Point extends GlobalPoint | LocalPoint>(
|
||||
point: Point,
|
||||
shape: GeometricShape<Point>,
|
||||
) => {
|
||||
switch (shape.type) {
|
||||
case "polygon":
|
||||
return polygonIncludesPoint(point, shape.data);
|
||||
case "line":
|
||||
return false;
|
||||
case "curve":
|
||||
return false;
|
||||
case "ellipse":
|
||||
return pointInEllipse(point, shape.data);
|
||||
case "polyline": {
|
||||
const polygon = polygonFromPoints(shape.data.flat());
|
||||
return polygonIncludesPoint(point, polygon);
|
||||
}
|
||||
case "polycurve": {
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
throw Error(`shape ${shape} is not implemented`);
|
||||
}
|
||||
};
|
||||
|
||||
// check if the given element is in the given bounds
|
||||
export const isPointInBounds = <Point extends GlobalPoint | LocalPoint>(
|
||||
point: Point,
|
||||
bounds: Polygon<Point>,
|
||||
) => {
|
||||
return polygonIncludesPoint(point, bounds);
|
||||
};
|
||||
|
||||
const pointOnPolycurve = <Point extends LocalPoint | GlobalPoint>(
|
||||
point: Point,
|
||||
polycurve: Polycurve<Point>,
|
||||
tolerance: number,
|
||||
) => {
|
||||
return polycurve.some((curve) => pointOnCurve(point, curve, tolerance));
|
||||
};
|
||||
|
||||
const cubicBezierEquation = <Point extends LocalPoint | GlobalPoint>(
|
||||
curve: Curve<Point>,
|
||||
) => {
|
||||
const [p0, p1, p2, p3] = curve;
|
||||
// B(t) = p0 * (1-t)^3 + 3p1 * t * (1-t)^2 + 3p2 * t^2 * (1-t) + p3 * t^3
|
||||
return (t: number, idx: number) =>
|
||||
Math.pow(1 - t, 3) * p3[idx] +
|
||||
3 * t * Math.pow(1 - t, 2) * p2[idx] +
|
||||
3 * Math.pow(t, 2) * (1 - t) * p1[idx] +
|
||||
p0[idx] * Math.pow(t, 3);
|
||||
};
|
||||
|
||||
const polyLineFromCurve = <Point extends LocalPoint | GlobalPoint>(
|
||||
curve: Curve<Point>,
|
||||
segments = 10,
|
||||
): Polyline<Point> => {
|
||||
const equation = cubicBezierEquation(curve);
|
||||
let startingPoint = [equation(0, 0), equation(0, 1)] as Point;
|
||||
const lineSegments: Polyline<Point> = [];
|
||||
let t = 0;
|
||||
const increment = 1 / segments;
|
||||
|
||||
for (let i = 0; i < segments; i++) {
|
||||
t += increment;
|
||||
if (t <= 1) {
|
||||
const nextPoint: Point = pointFrom(equation(t, 0), equation(t, 1));
|
||||
lineSegments.push(lineSegment(startingPoint, nextPoint));
|
||||
startingPoint = nextPoint;
|
||||
}
|
||||
}
|
||||
|
||||
return lineSegments;
|
||||
};
|
||||
|
||||
export const pointOnCurve = <Point extends LocalPoint | GlobalPoint>(
|
||||
point: Point,
|
||||
curve: Curve<Point>,
|
||||
threshold: number,
|
||||
) => {
|
||||
return pointOnPolyline(point, polyLineFromCurve(curve), threshold);
|
||||
};
|
||||
|
||||
export const pointOnPolyline = <Point extends LocalPoint | GlobalPoint>(
|
||||
point: Point,
|
||||
polyline: Polyline<Point>,
|
||||
threshold = 10e-5,
|
||||
) => {
|
||||
return polyline.some((line) => pointOnLineSegment(point, line, threshold));
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
export * from "./export";
|
||||
export * from "./withinBounds";
|
||||
export * from "./bbox";
|
||||
export { getCommonBounds } from "@excalidraw/element/bounds";
|
||||
export { getCommonBounds } from "@excalidraw/element";
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
type LocalPoint,
|
||||
} from "@excalidraw/math";
|
||||
|
||||
import { getElementAbsoluteCoords } from "@excalidraw/element/bounds";
|
||||
import { getElementAbsoluteCoords } from "@excalidraw/element";
|
||||
|
||||
import type {
|
||||
ElementsMap,
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
import {
|
||||
isLineSegment,
|
||||
lineSegment,
|
||||
pointFrom,
|
||||
type GlobalPoint,
|
||||
type LocalPoint,
|
||||
} from "@excalidraw/math";
|
||||
|
||||
import { isBounds } from "@excalidraw/element";
|
||||
|
||||
import type { Curve } from "@excalidraw/math";
|
||||
import type { LineSegment } from "@excalidraw/utils";
|
||||
|
||||
import type { Bounds } from "@excalidraw/element";
|
||||
|
||||
// The global data holder to collect the debug operations
|
||||
declare global {
|
||||
interface Window {
|
||||
visualDebug?: {
|
||||
data: DebugElement[][];
|
||||
currentFrame?: number;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export type DebugElement = {
|
||||
color: string;
|
||||
data: LineSegment<GlobalPoint> | Curve<GlobalPoint>;
|
||||
permanent: boolean;
|
||||
};
|
||||
|
||||
export const debugDrawCubicBezier = (
|
||||
c: Curve<GlobalPoint>,
|
||||
opts?: {
|
||||
color?: string;
|
||||
permanent?: boolean;
|
||||
},
|
||||
) => {
|
||||
addToCurrentFrame({
|
||||
color: opts?.color ?? "purple",
|
||||
permanent: !!opts?.permanent,
|
||||
data: c,
|
||||
});
|
||||
};
|
||||
|
||||
export const debugDrawLine = (
|
||||
segment: LineSegment<GlobalPoint> | LineSegment<GlobalPoint>[],
|
||||
opts?: {
|
||||
color?: string;
|
||||
permanent?: boolean;
|
||||
},
|
||||
) => {
|
||||
const segments = (
|
||||
isLineSegment(segment) ? [segment] : segment
|
||||
) as LineSegment<GlobalPoint>[];
|
||||
|
||||
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<GlobalPoint>(p[0] + xOffset - 10, p[1] + yOffset - 10),
|
||||
pointFrom<GlobalPoint>(p[0] + xOffset + 10, p[1] + yOffset + 10),
|
||||
),
|
||||
{
|
||||
color: opts?.color ?? "cyan",
|
||||
permanent: opts?.permanent,
|
||||
},
|
||||
);
|
||||
debugDrawLine(
|
||||
lineSegment(
|
||||
pointFrom<GlobalPoint>(p[0] + xOffset - 10, p[1] + yOffset + 10),
|
||||
pointFrom<GlobalPoint>(p[0] + xOffset + 10, p[1] + yOffset - 10),
|
||||
),
|
||||
{
|
||||
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<GlobalPoint>(bbox[0], bbox[1]),
|
||||
pointFrom<GlobalPoint>(bbox[2], bbox[1]),
|
||||
),
|
||||
lineSegment(
|
||||
pointFrom<GlobalPoint>(bbox[2], bbox[1]),
|
||||
pointFrom<GlobalPoint>(bbox[2], bbox[3]),
|
||||
),
|
||||
lineSegment(
|
||||
pointFrom<GlobalPoint>(bbox[2], bbox[3]),
|
||||
pointFrom<GlobalPoint>(bbox[0], bbox[3]),
|
||||
),
|
||||
lineSegment(
|
||||
pointFrom<GlobalPoint>(bbox[0], bbox[3]),
|
||||
pointFrom<GlobalPoint>(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<GlobalPoint>(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);
|
||||
};
|
||||
@@ -1,12 +1,12 @@
|
||||
import { arrayToMap } from "@excalidraw/common";
|
||||
import { getElementBounds } from "@excalidraw/element/bounds";
|
||||
import { getElementBounds } from "@excalidraw/element";
|
||||
import {
|
||||
isArrowElement,
|
||||
isExcalidrawElement,
|
||||
isFreeDrawElement,
|
||||
isLinearElement,
|
||||
isTextElement,
|
||||
} from "@excalidraw/element/typeChecks";
|
||||
} from "@excalidraw/element";
|
||||
import {
|
||||
rangeIncludesValue,
|
||||
pointFrom,
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
rangeInclusive,
|
||||
} from "@excalidraw/math";
|
||||
|
||||
import type { Bounds } from "@excalidraw/element/bounds";
|
||||
import type { Bounds } from "@excalidraw/element";
|
||||
import type {
|
||||
ExcalidrawElement,
|
||||
ExcalidrawFreeDrawElement,
|
||||
|
||||
Reference in New Issue
Block a user