mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Bounds refactor and duplication removal
This commit is contained in:
parent
7b4e989d65
commit
91b6057d9c
28 changed files with 431 additions and 147 deletions
|
@ -35,7 +35,7 @@ import {
|
|||
isHandToolActive,
|
||||
} from "../appState";
|
||||
import { DEFAULT_CANVAS_BACKGROUND_PICKS } from "../colors";
|
||||
import type { SceneBounds } from "../element/bounds";
|
||||
import type { ViewportBounds } from "../element/bounds";
|
||||
import { setCursor } from "../cursor";
|
||||
import { StoreAction } from "../store";
|
||||
import { clamp, point, roundToStep } from "../../math";
|
||||
|
@ -245,7 +245,7 @@ export const actionResetZoom = register({
|
|||
});
|
||||
|
||||
const zoomValueToFitBoundsOnViewport = (
|
||||
bounds: SceneBounds,
|
||||
bounds: ViewportBounds,
|
||||
viewportDimensions: { width: number; height: number },
|
||||
viewportZoomFactor: number = 1, // default to 1 if not provided
|
||||
) => {
|
||||
|
@ -271,7 +271,7 @@ export const zoomToFitBounds = ({
|
|||
minZoom = -Infinity,
|
||||
maxZoom = Infinity,
|
||||
}: {
|
||||
bounds: SceneBounds;
|
||||
bounds: ViewportBounds;
|
||||
canvasOffsets?: Offsets;
|
||||
appState: Readonly<AppState>;
|
||||
/** whether to fit content to viewport (beyond >100%) */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { ElementsMap, ExcalidrawElement } from "./element/types";
|
||||
import type { Bounds, ElementsMap, ExcalidrawElement } from "./element/types";
|
||||
import { newElementWith } from "./element/mutateElement";
|
||||
import { getCommonBounds, type Bounds } from "./element/bounds";
|
||||
import { getCommonBounds } from "./element/bounds";
|
||||
import { getMaximumGroups } from "./groups";
|
||||
|
||||
export interface Alignment {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import type { GlobalPoint, Radians } from "../../../math";
|
||||
import { point, pointRotateRads } from "../../../math";
|
||||
import { MIME_TYPES } from "../../constants";
|
||||
import type { Bounds } from "../../element/bounds";
|
||||
import { getElementAbsoluteCoords } from "../../element/bounds";
|
||||
import { hitElementBoundingBox } from "../../element/collision";
|
||||
import type {
|
||||
Bounds,
|
||||
ElementsMap,
|
||||
NonDeletedExcalidrawElement,
|
||||
} from "../../element/types";
|
||||
|
|
|
@ -1,5 +1,236 @@
|
|||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Test Transform > Test arrow bindings > should bind arrows to existing shapes when start / end provided with ids 1`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"backgroundColor": "#d8f5a2",
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "id45",
|
||||
"type": "arrow",
|
||||
},
|
||||
{
|
||||
"id": "id46",
|
||||
"type": "arrow",
|
||||
},
|
||||
],
|
||||
"customData": undefined,
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 300,
|
||||
"id": Any<String>,
|
||||
"index": "a0",
|
||||
"isDeleted": false,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#66a80f",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "ellipse",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
"versionNonce": Any<Number>,
|
||||
"width": 300,
|
||||
"x": 630,
|
||||
"y": 316,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Test Transform > Test arrow bindings > should bind arrows to existing shapes when start / end provided with ids 2`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "id46",
|
||||
"type": "arrow",
|
||||
},
|
||||
],
|
||||
"customData": undefined,
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 100,
|
||||
"id": Any<String>,
|
||||
"index": "a1",
|
||||
"isDeleted": false,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#9c36b5",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "diamond",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
"versionNonce": Any<Number>,
|
||||
"width": 140,
|
||||
"x": 96,
|
||||
"y": 400,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Test Transform > Test arrow bindings > should bind arrows to existing shapes when start / end provided with ids 3`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"customData": undefined,
|
||||
"elbowed": false,
|
||||
"endArrowhead": "arrow",
|
||||
"endBinding": {
|
||||
"elementId": "ellipse-1",
|
||||
"fixedPoint": null,
|
||||
"focus": -0.008153707962747813,
|
||||
"gap": 11.562288374879595,
|
||||
},
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 35,
|
||||
"id": Any<String>,
|
||||
"index": "a2",
|
||||
"isDeleted": false,
|
||||
"lastCommittedPoint": null,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"opacity": 100,
|
||||
"points": [
|
||||
[
|
||||
0.5,
|
||||
0.5,
|
||||
],
|
||||
[
|
||||
394.5,
|
||||
34.5,
|
||||
],
|
||||
],
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": Any<Number>,
|
||||
"startArrowhead": null,
|
||||
"startBinding": {
|
||||
"elementId": "id47",
|
||||
"fixedPoint": null,
|
||||
"focus": -0.08139534883720931,
|
||||
"gap": 1,
|
||||
},
|
||||
"strokeColor": "#1864ab",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "arrow",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
"versionNonce": Any<Number>,
|
||||
"width": 395,
|
||||
"x": 247,
|
||||
"y": 420,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Test Transform > Test arrow bindings > should bind arrows to existing shapes when start / end provided with ids 4`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"customData": undefined,
|
||||
"elbowed": false,
|
||||
"endArrowhead": "arrow",
|
||||
"endBinding": {
|
||||
"elementId": "ellipse-1",
|
||||
"fixedPoint": null,
|
||||
"focus": 0.10666666666666667,
|
||||
"gap": 3.8343264684446097,
|
||||
},
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 0,
|
||||
"id": Any<String>,
|
||||
"index": "a3",
|
||||
"isDeleted": false,
|
||||
"lastCommittedPoint": null,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"opacity": 100,
|
||||
"points": [
|
||||
[
|
||||
0.5,
|
||||
0,
|
||||
],
|
||||
[
|
||||
399.5,
|
||||
0,
|
||||
],
|
||||
],
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": Any<Number>,
|
||||
"startArrowhead": null,
|
||||
"startBinding": {
|
||||
"elementId": "diamond-1",
|
||||
"fixedPoint": null,
|
||||
"focus": 0,
|
||||
"gap": 5.2311437434718675,
|
||||
},
|
||||
"strokeColor": "#e67700",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "arrow",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
"versionNonce": Any<Number>,
|
||||
"width": 400,
|
||||
"x": 227,
|
||||
"y": 450,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Test Transform > Test arrow bindings > should bind arrows to existing shapes when start / end provided with ids 5`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": [
|
||||
{
|
||||
"id": "id45",
|
||||
"type": "arrow",
|
||||
},
|
||||
],
|
||||
"customData": undefined,
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 300,
|
||||
"id": Any<String>,
|
||||
"index": "a4",
|
||||
"isDeleted": false,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
"versionNonce": Any<Number>,
|
||||
"width": 300,
|
||||
"x": -53,
|
||||
"y": 270,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Test Transform > Test arrow bindings > should bind arrows to existing text elements when start / end provided with ids 1`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
|
|
|
@ -25,9 +25,9 @@ import type {
|
|||
ExcalidrawElbowArrowElement,
|
||||
FixedPoint,
|
||||
SceneElementsMap,
|
||||
Bounds,
|
||||
} from "./types";
|
||||
|
||||
import type { Bounds } from "./bounds";
|
||||
import { getCenterForBounds, getElementAbsoluteCoords } from "./bounds";
|
||||
import type { AppState } from "../types";
|
||||
import { isPointOnShape } from "../../utils/collision";
|
||||
|
|
|
@ -4,6 +4,7 @@ import type {
|
|||
ExcalidrawFreeDrawElement,
|
||||
ExcalidrawTextElementWithContainer,
|
||||
ElementsMap,
|
||||
Bounds,
|
||||
} from "./types";
|
||||
import rough from "roughjs/bin/rough";
|
||||
import type { Point as RoughPoint } from "roughjs/bin/geometry";
|
||||
|
@ -34,17 +35,7 @@ import { getCurvePathOps } from "../../utils/geometry/shape";
|
|||
|
||||
type MaybeQuadraticSolution = [number | null, number | null] | false;
|
||||
|
||||
/**
|
||||
* x and y position of top left corner, x and y position of bottom right corner
|
||||
*/
|
||||
export type Bounds = readonly [
|
||||
minX: number,
|
||||
minY: number,
|
||||
maxX: number,
|
||||
maxY: number,
|
||||
];
|
||||
|
||||
export type SceneBounds = readonly [
|
||||
export type ViewportBounds = readonly [
|
||||
sceneX: number,
|
||||
sceneY: number,
|
||||
sceneX2: number,
|
||||
|
@ -57,6 +48,7 @@ class ElementBounds {
|
|||
{
|
||||
bounds: Bounds;
|
||||
version: ExcalidrawElement["version"];
|
||||
versionNonce: ExcalidrawElement["versionNonce"];
|
||||
}
|
||||
>();
|
||||
|
||||
|
@ -66,6 +58,7 @@ class ElementBounds {
|
|||
if (
|
||||
cachedBounds?.version &&
|
||||
cachedBounds.version === element.version &&
|
||||
cachedBounds?.versionNonce === element.versionNonce &&
|
||||
// we don't invalidate cache when we update containers and not labels,
|
||||
// which is causing problems down the line. Fix TBA.
|
||||
!isBoundToContainer(element)
|
||||
|
@ -76,6 +69,7 @@ class ElementBounds {
|
|||
|
||||
ElementBounds.boundsCache.set(element, {
|
||||
version: element.version,
|
||||
versionNonce: element.versionNonce,
|
||||
bounds,
|
||||
});
|
||||
|
||||
|
@ -93,7 +87,7 @@ class ElementBounds {
|
|||
elementsMap,
|
||||
);
|
||||
if (isFreeDrawElement(element)) {
|
||||
const [minX, minY, maxX, maxY] = getBoundsFromPoints(
|
||||
const [minX, minY, maxX, maxY] = getBoundsFromFreeDrawPoints(
|
||||
element.points.map(([x, y]) =>
|
||||
pointRotateRads(
|
||||
point(x, y),
|
||||
|
@ -177,6 +171,20 @@ class ElementBounds {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the axis-aligned bounds of the given element in global / scene coordinates
|
||||
*
|
||||
* @param element The element to determine the bounding box for
|
||||
* @param elementsMap The elements map to retrieve attached elements (notably text label)
|
||||
* @returns The axis-aligned bounding box in scene (global coordinates)
|
||||
*/
|
||||
export const getElementBounds = (
|
||||
element: ExcalidrawElement,
|
||||
elementsMap: ElementsMap,
|
||||
): Bounds => {
|
||||
return ElementBounds.getBounds(element, elementsMap);
|
||||
};
|
||||
|
||||
// Scene -> Scene coords, but in x1,x2,y1,y2 format.
|
||||
//
|
||||
// If the element is created from right to left, the width is going to be negative
|
||||
|
@ -224,21 +232,6 @@ export const getElementAbsoluteCoords = (
|
|||
];
|
||||
};
|
||||
|
||||
export const getDiamondPoints = (element: ExcalidrawElement) => {
|
||||
// Here we add +1 to avoid these numbers to be 0
|
||||
// otherwise rough.js will throw an error complaining about it
|
||||
const topX = Math.floor(element.width / 2) + 1;
|
||||
const topY = 0;
|
||||
const rightX = element.width;
|
||||
const rightY = Math.floor(element.height / 2) + 1;
|
||||
const bottomX = topX;
|
||||
const bottomY = element.height;
|
||||
const leftX = 0;
|
||||
const leftY = rightY;
|
||||
|
||||
return [topX, topY, rightX, rightY, bottomX, bottomY, leftX, leftY];
|
||||
};
|
||||
|
||||
// reference: https://eliot-jones.com/2019/12/cubic-bezier-curve-bounding-boxes
|
||||
const getBezierValueForT = (
|
||||
t: number,
|
||||
|
@ -382,7 +375,7 @@ export const getMinMaxXYFromCurvePathOps = (
|
|||
return [minX, minY, maxX, maxY];
|
||||
};
|
||||
|
||||
export const getBoundsFromPoints = (
|
||||
const getBoundsFromFreeDrawPoints = (
|
||||
points: ExcalidrawFreeDrawElement["points"],
|
||||
): Bounds => {
|
||||
let minX = Infinity;
|
||||
|
@ -403,7 +396,7 @@ export const getBoundsFromPoints = (
|
|||
const getFreeDrawElementAbsoluteCoords = (
|
||||
element: ExcalidrawFreeDrawElement,
|
||||
): [number, number, number, number, number, number] => {
|
||||
const [minX, minY, maxX, maxY] = getBoundsFromPoints(element.points);
|
||||
const [minX, minY, maxX, maxY] = getBoundsFromFreeDrawPoints(element.points);
|
||||
const x1 = minX + element.x;
|
||||
const y1 = minY + element.y;
|
||||
const x2 = maxX + element.x;
|
||||
|
@ -496,13 +489,6 @@ const getLinearElementRotatedBounds = (
|
|||
return coords;
|
||||
};
|
||||
|
||||
export const getElementBounds = (
|
||||
element: ExcalidrawElement,
|
||||
elementsMap: ElementsMap,
|
||||
): Bounds => {
|
||||
return ElementBounds.getBounds(element, elementsMap);
|
||||
};
|
||||
|
||||
export const getCommonBounds = (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
elementsMap?: ElementsMap,
|
||||
|
@ -568,7 +554,7 @@ export const getResizedElementAbsoluteCoords = (
|
|||
|
||||
if (isFreeDrawElement(element)) {
|
||||
// Free Draw
|
||||
bounds = getBoundsFromPoints(points);
|
||||
bounds = getBoundsFromFreeDrawPoints(points);
|
||||
} else {
|
||||
// Line
|
||||
const gen = rough.generator();
|
||||
|
@ -651,7 +637,7 @@ export const getVisibleSceneBounds = ({
|
|||
width,
|
||||
height,
|
||||
zoom,
|
||||
}: AppState): SceneBounds => {
|
||||
}: AppState): ViewportBounds => {
|
||||
return [
|
||||
-scrollX,
|
||||
-scrollY,
|
||||
|
|
|
@ -40,31 +40,33 @@ export const distanceToBindableElement = (
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the distance of a point and the provided rectangular-shaped element,
|
||||
* accounting for roundness and rotation
|
||||
*
|
||||
* @param element The rectanguloid element
|
||||
* @param p The point to consider
|
||||
* @returns The eucledian distance to the outline of the rectanguloid element
|
||||
*/
|
||||
export const distanceToRectangleElement = (
|
||||
element: ExcalidrawRectanguloidElement,
|
||||
p: GlobalPoint,
|
||||
) => {
|
||||
const center = point(
|
||||
element.x + element.width / 2,
|
||||
element.y + element.height / 2,
|
||||
);
|
||||
const r = rectangle(
|
||||
pointRotateRads(
|
||||
point(element.x, element.y),
|
||||
center,
|
||||
radians(element.angle),
|
||||
),
|
||||
pointRotateRads(
|
||||
point(element.x + element.width, element.y + element.height),
|
||||
center,
|
||||
radians(element.angle),
|
||||
),
|
||||
point(element.x, element.y),
|
||||
point(element.x + element.width, element.y + element.height),
|
||||
);
|
||||
// To emulate a rotated rectangle we rotate the point in the inverse angle
|
||||
// instead. It's all the same distance-wise.
|
||||
const rotatedPoint = pointRotateRads(
|
||||
p,
|
||||
point(element.x + element.width / 2, element.y + element.height / 2),
|
||||
radians(-element.angle),
|
||||
);
|
||||
const roundness = getCornerRadius(
|
||||
Math.min(element.width, element.height),
|
||||
element,
|
||||
);
|
||||
const rotatedPoint = pointRotateRads(p, center, element.angle);
|
||||
const sideDistances = [
|
||||
segment(
|
||||
point(r[0][0] + roundness, r[0][1]),
|
||||
|
@ -116,10 +118,22 @@ export const distanceToRectangleElement = (
|
|||
return Math.min(...[...sideDistances, ...cornerDistances]);
|
||||
};
|
||||
|
||||
const roundedCutoffSegment = (
|
||||
/**
|
||||
* Shortens a segment on both ends to accomodate the arc in the rounded
|
||||
* diamond shape
|
||||
*
|
||||
* @param s The segment to shorten
|
||||
* @param r The radius to shorten by
|
||||
* @returns The segment shortened on both ends by the same radius
|
||||
*/
|
||||
const createDiamondSide = (
|
||||
s: Segment<GlobalPoint>,
|
||||
r: number,
|
||||
): Segment<GlobalPoint> => {
|
||||
if (r === 0) {
|
||||
return s;
|
||||
}
|
||||
|
||||
const t = (4 * r) / Math.sqrt(2);
|
||||
|
||||
return segment(
|
||||
|
@ -128,17 +142,36 @@ const roundedCutoffSegment = (
|
|||
);
|
||||
};
|
||||
|
||||
const diamondArc = (left: GlobalPoint, right: GlobalPoint, r: number) => {
|
||||
const c = point((left[0] + right[0]) / 2, left[1]);
|
||||
/**
|
||||
* Creates an arc for the given roundness and position by taking the start
|
||||
* and end positions and determining the angle points on the hypotethical
|
||||
* circle with center point between start and end and raidus equals provided
|
||||
* roundness. I.e. the created arc is gobal point-aware, or "rotated" in-place.
|
||||
*
|
||||
* @param start
|
||||
* @param end
|
||||
* @param r
|
||||
* @returns
|
||||
*/
|
||||
const createDiamondArc = (start: GlobalPoint, end: GlobalPoint, r: number) => {
|
||||
const c = point((start[0] + end[0]) / 2, start[1]);
|
||||
|
||||
return arc(
|
||||
c,
|
||||
r,
|
||||
radians(Math.asin((left[1] - c[1]) / r)),
|
||||
radians(Math.asin((right[1] - c[1]) / r)),
|
||||
radians(Math.asin((start[1] - c[1]) / r)),
|
||||
radians(Math.asin((end[1] - c[1]) / r)),
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the distance of a point and the provided diamond element, accounting
|
||||
* for roundness and rotation
|
||||
*
|
||||
* @param element The diamond element
|
||||
* @param p The point to consider
|
||||
* @returns The eucledian distance to the outline of the diamond
|
||||
*/
|
||||
export const distanceToDiamondElement = (
|
||||
element: ExcalidrawDiamondElement,
|
||||
p: GlobalPoint,
|
||||
|
@ -151,31 +184,19 @@ export const distanceToDiamondElement = (
|
|||
Math.min(element.width, element.height),
|
||||
element,
|
||||
);
|
||||
const rotatedPoint = pointRotateRads(p, center, element.angle);
|
||||
const top = pointRotateRads<GlobalPoint>(
|
||||
// Rotate the point to the inverse direction to simulate the rotated diamond
|
||||
// points. It's all the same distance-wise.
|
||||
const rotatedPoint = pointRotateRads(p, center, radians(-element.angle));
|
||||
const [top, right, bottom, left]: GlobalPoint[] = [
|
||||
point(element.x + element.width / 2, element.y),
|
||||
center,
|
||||
element.angle,
|
||||
);
|
||||
const right = pointRotateRads<GlobalPoint>(
|
||||
point(element.x + element.width, element.y + element.height / 2),
|
||||
center,
|
||||
element.angle,
|
||||
);
|
||||
const bottom = pointRotateRads<GlobalPoint>(
|
||||
point(element.x + element.width / 2, element.y + element.height),
|
||||
center,
|
||||
element.angle,
|
||||
);
|
||||
const left = pointRotateRads<GlobalPoint>(
|
||||
point(element.x, element.y + element.height / 2),
|
||||
center,
|
||||
element.angle,
|
||||
);
|
||||
const topRight = roundedCutoffSegment(segment(top, right), roundness);
|
||||
const bottomRight = roundedCutoffSegment(segment(right, bottom), roundness);
|
||||
const bottomLeft = roundedCutoffSegment(segment(bottom, left), roundness);
|
||||
const topLeft = roundedCutoffSegment(segment(left, top), roundness);
|
||||
];
|
||||
const topRight = createDiamondSide(segment(top, right), roundness);
|
||||
const bottomRight = createDiamondSide(segment(right, bottom), roundness);
|
||||
const bottomLeft = createDiamondSide(segment(bottom, left), roundness);
|
||||
const topLeft = createDiamondSide(segment(left, top), roundness);
|
||||
|
||||
return Math.min(
|
||||
...[
|
||||
|
@ -184,16 +205,24 @@ export const distanceToDiamondElement = (
|
|||
),
|
||||
...(roundness > 0
|
||||
? [
|
||||
diamondArc(topLeft[1], topRight[0], roundness),
|
||||
diamondArc(topRight[1], bottomRight[0], roundness),
|
||||
diamondArc(bottomRight[1], bottomLeft[0], roundness),
|
||||
diamondArc(bottomLeft[1], topLeft[0], roundness),
|
||||
createDiamondArc(topLeft[1], topRight[0], roundness),
|
||||
createDiamondArc(topRight[1], bottomRight[0], roundness),
|
||||
createDiamondArc(bottomRight[1], bottomLeft[0], roundness),
|
||||
createDiamondArc(bottomLeft[1], topLeft[0], roundness),
|
||||
].map((a) => arcDistanceFromPoint(a, rotatedPoint))
|
||||
: []),
|
||||
],
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the distance of a point and the provided ellipse element, accounting
|
||||
* for roundness and rotation
|
||||
*
|
||||
* @param element The ellipse element
|
||||
* @param p The point to consider
|
||||
* @returns The eucledian distance to the outline of the ellipse
|
||||
*/
|
||||
export const distanceToEllipseElement = (
|
||||
element: ExcalidrawEllipseElement,
|
||||
p: GlobalPoint,
|
||||
|
@ -203,6 +232,7 @@ export const distanceToEllipseElement = (
|
|||
element.y + element.height / 2,
|
||||
);
|
||||
return ellipseDistanceFromPoint(
|
||||
// Instead of rotating the ellipse, rotate the point to the inverse angle
|
||||
pointRotateRads(p, center, radians(-element.angle)),
|
||||
ellipse(center, element.width / 2, element.height / 2),
|
||||
);
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { updateBoundElements } from "./binding";
|
||||
import type { Bounds } from "./bounds";
|
||||
import { getCommonBounds } from "./bounds";
|
||||
import { mutateElement } from "./mutateElement";
|
||||
import { getPerfectElementSize } from "./sizeHelpers";
|
||||
import type { NonDeletedExcalidrawElement } from "./types";
|
||||
import type { Bounds, NonDeletedExcalidrawElement } from "./types";
|
||||
import type {
|
||||
AppState,
|
||||
NormalizedZoomValue,
|
||||
|
|
|
@ -13,8 +13,8 @@ import {
|
|||
radiansToDegrees,
|
||||
triangleIncludesPoint,
|
||||
} from "../../math";
|
||||
import { getCenterForBounds, type Bounds } from "./bounds";
|
||||
import type { ExcalidrawBindableElement } from "./types";
|
||||
import { getCenterForBounds } from "./bounds";
|
||||
import type { Bounds, ExcalidrawBindableElement } from "./types";
|
||||
|
||||
export const HEADING_RIGHT = [1, 0] as Heading;
|
||||
export const HEADING_DOWN = [0, 1] as Heading;
|
||||
|
|
|
@ -19,9 +19,9 @@ export {
|
|||
getElementAbsoluteCoords,
|
||||
getElementBounds,
|
||||
getCommonBounds,
|
||||
getDiamondPoints,
|
||||
getClosestElementBounds,
|
||||
} from "./bounds";
|
||||
export { getDiamondPoints } from "../scene/Shape";
|
||||
|
||||
export {
|
||||
OMIT_SIDES_FOR_MULTIPLE_ELEMENTS,
|
||||
|
|
|
@ -10,9 +10,9 @@ import type {
|
|||
OrderedExcalidrawElement,
|
||||
FixedPointBinding,
|
||||
SceneElementsMap,
|
||||
Bounds,
|
||||
} from "./types";
|
||||
import { getElementAbsoluteCoords, getLockedLinearCursorAlignSize } from ".";
|
||||
import type { Bounds } from "./bounds";
|
||||
import { getElementPointsCoords, getMinMaxXYFromCurvePathOps } from "./bounds";
|
||||
import type {
|
||||
AppState,
|
||||
|
|
|
@ -3,6 +3,7 @@ import type {
|
|||
PointerType,
|
||||
NonDeletedExcalidrawElement,
|
||||
ElementsMap,
|
||||
Bounds,
|
||||
} from "./types";
|
||||
|
||||
import type {
|
||||
|
@ -17,7 +18,6 @@ import {
|
|||
canResizeFromSides,
|
||||
} from "./transformHandles";
|
||||
import type { AppState, Device, Zoom } from "../types";
|
||||
import type { Bounds } from "./bounds";
|
||||
import { getElementAbsoluteCoords } from "./bounds";
|
||||
import { SIDE_RESIZING_THRESHOLD } from "../constants";
|
||||
import { isLinearElement } from "./typeChecks";
|
||||
|
|
|
@ -24,7 +24,6 @@ import {
|
|||
getGlobalFixedPointForBindableElement,
|
||||
snapToMid,
|
||||
} from "./binding";
|
||||
import type { Bounds } from "./bounds";
|
||||
import { distanceToBindableElement } from "./distance";
|
||||
import type { Heading } from "./heading";
|
||||
import {
|
||||
|
@ -40,6 +39,7 @@ import type { ElementUpdate } from "./mutateElement";
|
|||
import { mutateElement } from "./mutateElement";
|
||||
import { isBindableElement, isRectanguloidElement } from "./typeChecks";
|
||||
import type {
|
||||
Bounds,
|
||||
ExcalidrawElbowArrowElement,
|
||||
NonDeletedSceneElementsMap,
|
||||
SceneElementsMap,
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import type {
|
||||
Bounds,
|
||||
ElementsMap,
|
||||
ExcalidrawElement,
|
||||
NonDeletedExcalidrawElement,
|
||||
PointerType,
|
||||
} from "./types";
|
||||
|
||||
import type { Bounds } from "./bounds";
|
||||
import { getElementAbsoluteCoords } from "./bounds";
|
||||
import type { Device, InteractiveCanvasAppState, Zoom } from "../types";
|
||||
import {
|
||||
|
|
|
@ -2,7 +2,6 @@ import { ROUNDNESS } from "../constants";
|
|||
import type { ElementOrToolType } from "../types";
|
||||
import type { MarkNonNullable } from "../utility-types";
|
||||
import { assertNever } from "../utils";
|
||||
import type { Bounds } from "./bounds";
|
||||
import type {
|
||||
ExcalidrawElement,
|
||||
ExcalidrawTextElement,
|
||||
|
@ -26,6 +25,7 @@ import type {
|
|||
PointBinding,
|
||||
FixedPointBinding,
|
||||
ExcalidrawFlowchartNodeElement,
|
||||
Bounds,
|
||||
} from "./types";
|
||||
|
||||
export const isInitializedImageElement = (
|
||||
|
|
|
@ -373,3 +373,13 @@ export type NonDeletedSceneElementsMap = Map<
|
|||
export type ElementsMapOrArray =
|
||||
| readonly ExcalidrawElement[]
|
||||
| Readonly<ElementsMap>;
|
||||
|
||||
/**
|
||||
* Axis-aligned bounding box (i.e. no rotation)
|
||||
*/
|
||||
export type Bounds = readonly [
|
||||
minX: number,
|
||||
minY: number,
|
||||
maxX: number,
|
||||
maxY: number,
|
||||
];
|
||||
|
|
|
@ -25,7 +25,7 @@ import type {
|
|||
import { getElementsWithinSelection, getSelectedElements } from "./scene";
|
||||
import { getElementsInGroup, selectGroupsFromGivenElements } from "./groups";
|
||||
import type { ExcalidrawElementsIncludingDeleted } from "./scene/Scene";
|
||||
import { elementsOverlappingBBox } from "../utils/";
|
||||
import { elementsOverlappingBounds } from "../utils/";
|
||||
import {
|
||||
isFrameElement,
|
||||
isFrameLikeElement,
|
||||
|
@ -863,7 +863,7 @@ export const getElementsOverlappingFrame = (
|
|||
frame: ExcalidrawFrameLikeElement,
|
||||
) => {
|
||||
return (
|
||||
elementsOverlappingBBox({
|
||||
elementsOverlappingBounds({
|
||||
elements,
|
||||
bounds: frame,
|
||||
type: "overlap",
|
||||
|
|
|
@ -284,9 +284,9 @@ export { convertToExcalidrawElements } from "./data/transform";
|
|||
export { getCommonBounds, getVisibleSceneBounds } from "./element/bounds";
|
||||
|
||||
export {
|
||||
elementsOverlappingBBox,
|
||||
elementsOverlappingBounds as elementsOverlappingBBox,
|
||||
isElementInsideBBox,
|
||||
elementPartiallyOverlapsWithOrContainsBBox,
|
||||
elementPartiallyOverlapsWithOrContainsBounds as elementPartiallyOverlapsWithOrContainsBBox,
|
||||
} from "../utils/withinBounds";
|
||||
|
||||
export { DiagramToCodePlugin } from "./components/DiagramToCodePlugin/DiagramToCodePlugin";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { Point as RoughPoint } from "roughjs/bin/geometry";
|
||||
import type { Drawable, Options } from "roughjs/bin/core";
|
||||
import type { RoughGenerator } from "roughjs/bin/generator";
|
||||
import { getArrowheadPoints, getDiamondPoints } from "../element";
|
||||
import { getArrowheadPoints } from "../element";
|
||||
import type { ElementShapes } from "./types";
|
||||
import type {
|
||||
ExcalidrawElement,
|
||||
|
@ -278,6 +278,21 @@ const getArrowheadShapes = (
|
|||
}
|
||||
};
|
||||
|
||||
export const getDiamondPoints = (element: ExcalidrawElement) => {
|
||||
// Here we add +1 to avoid these numbers to be 0
|
||||
// otherwise rough.js will throw an error complaining about it
|
||||
const topX = Math.floor(element.width / 2) + 1;
|
||||
const topY = 0;
|
||||
const rightX = element.width;
|
||||
const rightY = Math.floor(element.height / 2) + 1;
|
||||
const bottomX = topX;
|
||||
const bottomY = element.height;
|
||||
const leftX = 0;
|
||||
const leftY = rightY;
|
||||
|
||||
return [topX, topY, rightX, rightY, bottomX, bottomY, leftX, leftY];
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates the roughjs shape for given element.
|
||||
*
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import rough from "roughjs/bin/rough";
|
||||
import type {
|
||||
Bounds,
|
||||
ExcalidrawElement,
|
||||
ExcalidrawFrameLikeElement,
|
||||
ExcalidrawTextElement,
|
||||
NonDeletedExcalidrawElement,
|
||||
NonDeletedSceneElementsMap,
|
||||
} from "../element/types";
|
||||
import type { Bounds } from "../element/bounds";
|
||||
import { getCommonBounds, getElementAbsoluteCoords } from "../element/bounds";
|
||||
import { renderSceneToSvg } from "../renderer/staticSvgScene";
|
||||
import { arrayToMap, getFontString, toBrandedType } from "../utils";
|
||||
|
|
|
@ -36,11 +36,11 @@ import {
|
|||
ROUNDNESS,
|
||||
} from "./constants";
|
||||
import { getElementAbsoluteCoords } from "./element";
|
||||
import type { Bounds } from "./element/bounds";
|
||||
import { shouldTestInside } from "./element/collision";
|
||||
import { LinearElementEditor } from "./element/linearElementEditor";
|
||||
import { getBoundTextElement } from "./element/textElement";
|
||||
import type {
|
||||
Bounds,
|
||||
ElementsMap,
|
||||
ExcalidrawElement,
|
||||
ExcalidrawLinearElement,
|
||||
|
|
|
@ -8,7 +8,6 @@ import {
|
|||
type GlobalPoint,
|
||||
} from "../math";
|
||||
import { TOOL_TYPE } from "./constants";
|
||||
import type { Bounds } from "./element/bounds";
|
||||
import {
|
||||
getCommonBounds,
|
||||
getDraggedElementsBounds,
|
||||
|
@ -17,6 +16,7 @@ import {
|
|||
import type { MaybeTransformHandleType } from "./element/transformHandles";
|
||||
import { isBoundToContainer, isFrameLikeElement } from "./element/typeChecks";
|
||||
import type {
|
||||
Bounds,
|
||||
ElementsMap,
|
||||
ExcalidrawElement,
|
||||
NonDeletedExcalidrawElement,
|
||||
|
|
|
@ -4,11 +4,11 @@ import { render } from "./test-utils";
|
|||
import { reseed } from "../random";
|
||||
import { UI, Keyboard, Pointer } from "./helpers/ui";
|
||||
import type {
|
||||
Bounds,
|
||||
ExcalidrawElbowArrowElement,
|
||||
ExcalidrawFreeDrawElement,
|
||||
ExcalidrawLinearElement,
|
||||
} from "../element/types";
|
||||
import type { Bounds } from "../element/bounds";
|
||||
import { getElementPointsCoords } from "../element/bounds";
|
||||
import { Excalidraw } from "../index";
|
||||
import { API } from "./helpers/api";
|
||||
|
@ -895,7 +895,7 @@ describe("multiple selection", () => {
|
|||
expect(rightBoundArrow.height).toBeCloseTo(0);
|
||||
expect(rightBoundArrow.angle).toEqual(0);
|
||||
expect(rightBoundArrow.startBinding).toBeNull();
|
||||
expect(rightBoundArrow.endBinding?.gap).toBeCloseTo(8.0952);
|
||||
expect(rightBoundArrow.endBinding?.gap).toBeCloseTo(7.0952);
|
||||
expect(rightBoundArrow.endBinding?.elementId).toBe(
|
||||
rightArrowBinding.elementId,
|
||||
);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { Segment } from "../math";
|
||||
import { isSegment, segment, point, type GlobalPoint } from "../math";
|
||||
import type { Bounds } from "./element/bounds";
|
||||
import { isBounds } from "./element/typeChecks";
|
||||
import type { Bounds } from "./element/types";
|
||||
|
||||
// The global data holder to collect the debug operations
|
||||
declare global {
|
||||
|
|
|
@ -102,6 +102,22 @@ export function pointRotateRads<Point extends GenericPoint>(
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate multiple points around a common center via the same angle in radians
|
||||
*
|
||||
* @param p The point array to rotate
|
||||
* @param c The common center point
|
||||
* @param angle The common angle to rotate by
|
||||
* @returns The array of rotated points
|
||||
*/
|
||||
function pointsRotateRads<Point extends GenericPoint>(
|
||||
p: Point[],
|
||||
c: Point,
|
||||
angle: Radians,
|
||||
): Point[] {
|
||||
return p.map((x, idx) => pointRotateRads(x, c, angle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Roate a point by [angle] degree.
|
||||
*
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import type { Bounds } from "../excalidraw/element/bounds";
|
||||
import type { Bounds } from "../excalidraw/element/types";
|
||||
import { API } from "../excalidraw/tests/helpers/api";
|
||||
import {
|
||||
elementPartiallyOverlapsWithOrContainsBBox,
|
||||
elementsOverlappingBBox,
|
||||
elementPartiallyOverlapsWithOrContainsBounds,
|
||||
elementsOverlappingBounds,
|
||||
isElementInsideBBox,
|
||||
} from "./withinBounds";
|
||||
|
||||
|
@ -99,13 +99,13 @@ describe("elementPartiallyOverlapsWithOrContainsBBox()", () => {
|
|||
|
||||
// bbox contains element
|
||||
expect(
|
||||
elementPartiallyOverlapsWithOrContainsBBox(
|
||||
elementPartiallyOverlapsWithOrContainsBounds(
|
||||
makeElement(0, 0, 100, 100),
|
||||
bbox,
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
elementPartiallyOverlapsWithOrContainsBBox(
|
||||
elementPartiallyOverlapsWithOrContainsBounds(
|
||||
makeElement(10, 10, 90, 90),
|
||||
bbox,
|
||||
),
|
||||
|
@ -113,7 +113,7 @@ describe("elementPartiallyOverlapsWithOrContainsBBox()", () => {
|
|||
|
||||
// element contains bbox
|
||||
expect(
|
||||
elementPartiallyOverlapsWithOrContainsBBox(
|
||||
elementPartiallyOverlapsWithOrContainsBounds(
|
||||
makeElement(-10, -10, 110, 110),
|
||||
bbox,
|
||||
),
|
||||
|
@ -121,28 +121,28 @@ describe("elementPartiallyOverlapsWithOrContainsBBox()", () => {
|
|||
|
||||
// element overlaps bbox from top-left
|
||||
expect(
|
||||
elementPartiallyOverlapsWithOrContainsBBox(
|
||||
elementPartiallyOverlapsWithOrContainsBounds(
|
||||
makeElement(-10, -10, 100, 100),
|
||||
bbox,
|
||||
),
|
||||
).toBe(true);
|
||||
// element overlaps bbox from top-right
|
||||
expect(
|
||||
elementPartiallyOverlapsWithOrContainsBBox(
|
||||
elementPartiallyOverlapsWithOrContainsBounds(
|
||||
makeElement(90, -10, 100, 100),
|
||||
bbox,
|
||||
),
|
||||
).toBe(true);
|
||||
// element overlaps bbox from bottom-left
|
||||
expect(
|
||||
elementPartiallyOverlapsWithOrContainsBBox(
|
||||
elementPartiallyOverlapsWithOrContainsBounds(
|
||||
makeElement(-10, 90, 100, 100),
|
||||
bbox,
|
||||
),
|
||||
).toBe(true);
|
||||
// element overlaps bbox from bottom-right
|
||||
expect(
|
||||
elementPartiallyOverlapsWithOrContainsBBox(
|
||||
elementPartiallyOverlapsWithOrContainsBounds(
|
||||
makeElement(90, 90, 100, 100),
|
||||
bbox,
|
||||
),
|
||||
|
@ -154,7 +154,7 @@ describe("elementPartiallyOverlapsWithOrContainsBBox()", () => {
|
|||
|
||||
// outside diagonally
|
||||
expect(
|
||||
elementPartiallyOverlapsWithOrContainsBBox(
|
||||
elementPartiallyOverlapsWithOrContainsBounds(
|
||||
makeElement(110, 110, 100, 100),
|
||||
bbox,
|
||||
),
|
||||
|
@ -162,28 +162,28 @@ describe("elementPartiallyOverlapsWithOrContainsBBox()", () => {
|
|||
|
||||
// outside on the left
|
||||
expect(
|
||||
elementPartiallyOverlapsWithOrContainsBBox(
|
||||
elementPartiallyOverlapsWithOrContainsBounds(
|
||||
makeElement(-110, 10, 50, 50),
|
||||
bbox,
|
||||
),
|
||||
).toBe(false);
|
||||
// outside on the right
|
||||
expect(
|
||||
elementPartiallyOverlapsWithOrContainsBBox(
|
||||
elementPartiallyOverlapsWithOrContainsBounds(
|
||||
makeElement(110, 10, 50, 50),
|
||||
bbox,
|
||||
),
|
||||
).toBe(false);
|
||||
// outside on the top
|
||||
expect(
|
||||
elementPartiallyOverlapsWithOrContainsBBox(
|
||||
elementPartiallyOverlapsWithOrContainsBounds(
|
||||
makeElement(10, -110, 50, 50),
|
||||
bbox,
|
||||
),
|
||||
).toBe(false);
|
||||
// outside on the bottom
|
||||
expect(
|
||||
elementPartiallyOverlapsWithOrContainsBBox(
|
||||
elementPartiallyOverlapsWithOrContainsBounds(
|
||||
makeElement(10, 110, 50, 50),
|
||||
bbox,
|
||||
),
|
||||
|
@ -201,7 +201,7 @@ describe("elementsOverlappingBBox()", () => {
|
|||
const rectOverlappingTopLeft = makeElement(-10, -10, 50, 50);
|
||||
|
||||
expect(
|
||||
elementsOverlappingBBox({
|
||||
elementsOverlappingBounds({
|
||||
bounds: bbox,
|
||||
type: "overlap",
|
||||
elements: [
|
||||
|
@ -223,7 +223,7 @@ describe("elementsOverlappingBBox()", () => {
|
|||
const rectOverlappingTopLeft = makeElement(-10, -10, 50, 50);
|
||||
|
||||
expect(
|
||||
elementsOverlappingBBox({
|
||||
elementsOverlappingBounds({
|
||||
bounds: bbox,
|
||||
type: "contain",
|
||||
elements: [
|
||||
|
@ -245,7 +245,7 @@ describe("elementsOverlappingBBox()", () => {
|
|||
const rectOverlappingTopLeft = makeElement(-10, -10, 50, 50);
|
||||
|
||||
expect(
|
||||
elementsOverlappingBBox({
|
||||
elementsOverlappingBounds({
|
||||
bounds: bbox,
|
||||
type: "inside",
|
||||
elements: [
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type {
|
||||
Bounds,
|
||||
ExcalidrawElement,
|
||||
ExcalidrawFreeDrawElement,
|
||||
ExcalidrawLinearElement,
|
||||
|
@ -11,7 +12,6 @@ import {
|
|||
isLinearElement,
|
||||
isTextElement,
|
||||
} from "../excalidraw/element/typeChecks";
|
||||
import type { Bounds } from "../excalidraw/element/bounds";
|
||||
import { getElementBounds } from "../excalidraw/element/bounds";
|
||||
import { arrayToMap } from "../excalidraw/utils";
|
||||
import type { LocalPoint } from "../math";
|
||||
|
@ -22,15 +22,10 @@ import {
|
|||
rangeInclusive,
|
||||
} from "../math";
|
||||
|
||||
type Element = NonDeletedExcalidrawElement;
|
||||
type Elements = readonly NonDeletedExcalidrawElement[];
|
||||
|
||||
type Points = readonly LocalPoint[];
|
||||
|
||||
/** @returns vertices relative to element's top-left [0,0] position */
|
||||
const getNonLinearElementRelativePoints = (
|
||||
element: Exclude<
|
||||
Element,
|
||||
NonDeletedExcalidrawElement,
|
||||
ExcalidrawLinearElement | ExcalidrawFreeDrawElement
|
||||
>,
|
||||
): [
|
||||
|
@ -56,14 +51,16 @@ const getNonLinearElementRelativePoints = (
|
|||
};
|
||||
|
||||
/** @returns vertices relative to element's top-left [0,0] position */
|
||||
const getElementRelativePoints = (element: ExcalidrawElement): Points => {
|
||||
const getElementRelativePoints = (
|
||||
element: ExcalidrawElement,
|
||||
): readonly LocalPoint[] => {
|
||||
if (isLinearElement(element) || isFreeDrawElement(element)) {
|
||||
return element.points;
|
||||
}
|
||||
return getNonLinearElementRelativePoints(element);
|
||||
};
|
||||
|
||||
const getMinMaxPoints = (points: Points) => {
|
||||
const getMinMaxPoints = (points: readonly LocalPoint[]) => {
|
||||
const ret = points.reduce(
|
||||
(limits, [x, y]) => {
|
||||
limits.minY = Math.min(limits.minY, y);
|
||||
|
@ -90,7 +87,7 @@ const getMinMaxPoints = (points: Points) => {
|
|||
return ret;
|
||||
};
|
||||
|
||||
const getRotatedBBox = (element: Element): Bounds => {
|
||||
const getRotatedBBox = (element: NonDeletedExcalidrawElement): Bounds => {
|
||||
const points = getElementRelativePoints(element);
|
||||
|
||||
const { cx, cy } = getMinMaxPoints(points);
|
||||
|
@ -110,7 +107,7 @@ const getRotatedBBox = (element: Element): Bounds => {
|
|||
};
|
||||
|
||||
export const isElementInsideBBox = (
|
||||
element: Element,
|
||||
element: NonDeletedExcalidrawElement,
|
||||
bbox: Bounds,
|
||||
eitherDirection = false,
|
||||
): boolean => {
|
||||
|
@ -138,8 +135,8 @@ export const isElementInsideBBox = (
|
|||
);
|
||||
};
|
||||
|
||||
export const elementPartiallyOverlapsWithOrContainsBBox = (
|
||||
element: Element,
|
||||
export const elementPartiallyOverlapsWithOrContainsBounds = (
|
||||
element: NonDeletedExcalidrawElement,
|
||||
bbox: Bounds,
|
||||
): boolean => {
|
||||
const elementBBox = getRotatedBBox(element);
|
||||
|
@ -158,13 +155,13 @@ export const elementPartiallyOverlapsWithOrContainsBBox = (
|
|||
);
|
||||
};
|
||||
|
||||
export const elementsOverlappingBBox = ({
|
||||
export const elementsOverlappingBounds = ({
|
||||
elements,
|
||||
bounds,
|
||||
type,
|
||||
errorMargin = 0,
|
||||
}: {
|
||||
elements: Elements;
|
||||
elements: readonly NonDeletedExcalidrawElement[];
|
||||
bounds: Bounds | ExcalidrawElement;
|
||||
/** safety offset. Defaults to 0. */
|
||||
errorMargin?: number;
|
||||
|
@ -194,7 +191,7 @@ export const elementsOverlappingBBox = ({
|
|||
|
||||
const isOverlaping =
|
||||
type === "overlap"
|
||||
? elementPartiallyOverlapsWithOrContainsBBox(element, adjustedBBox)
|
||||
? elementPartiallyOverlapsWithOrContainsBounds(element, adjustedBBox)
|
||||
: type === "inside"
|
||||
? isElementInsideBBox(element, adjustedBBox)
|
||||
: isElementInsideBBox(element, adjustedBBox, true);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue