diff --git a/packages/excalidraw/actions/actionFlip.ts b/packages/excalidraw/actions/actionFlip.ts index 6b75b8facd..aba4ec2316 100644 --- a/packages/excalidraw/actions/actionFlip.ts +++ b/packages/excalidraw/actions/actionFlip.ts @@ -1,6 +1,6 @@ import { register } from "./register"; import { getSelectedElements } from "../scene"; -import { getNonDeletedElements } from "../element"; +import { getCommonBounds, getNonDeletedElements } from "../element"; import type { ExcalidrawArrowElement, ExcalidrawElbowArrowElement, @@ -12,7 +12,6 @@ import { resizeMultipleElements } from "../element/resizeElements"; import type { AppClassProperties, AppState } from "../types"; import { arrayToMap } from "../utils"; import { CODES, KEYS } from "../keys"; -import { getCommonBoundingBox } from "../element/bounds"; import { bindOrUnbindLinearElements, isBindingEnabled, @@ -132,8 +131,9 @@ const flipElements = ( }); } - const { minX, minY, maxX, maxY, midX, midY } = - getCommonBoundingBox(selectedElements); + const [minX, minY, maxX, maxY] = getCommonBounds(selectedElements); + const midX = (minX + maxX) / 2; + const midY = (minY + maxY) / 2; resizeMultipleElements( elementsMap, @@ -175,8 +175,11 @@ const flipElements = ( { elbowArrows: [], otherElements: [] }, ); - const { midX: newMidX, midY: newMidY } = - getCommonBoundingBox(selectedElements); + const [newMinX, newMinY, newMaxX, newMaxY] = + getCommonBounds(selectedElements); + const newMidX = (newMinX + newMaxX) / 2; + const newMidY = (newMinY + newMaxY) / 2; + const [diffX, diffY] = [midX - newMidX, midY - newMidY]; otherElements.forEach((element) => mutateElement(element, { diff --git a/packages/excalidraw/align.ts b/packages/excalidraw/align.ts index 5abebea21e..98dcc4d7b3 100644 --- a/packages/excalidraw/align.ts +++ b/packages/excalidraw/align.ts @@ -1,7 +1,6 @@ import type { ElementsMap, ExcalidrawElement } from "./element/types"; import { newElementWith } from "./element/mutateElement"; -import type { BoundingBox } from "./element/bounds"; -import { getCommonBoundingBox } from "./element/bounds"; +import { getCommonBounds, type Bounds } from "./element/bounds"; import { getMaximumGroups } from "./groups"; export interface Alignment { @@ -18,14 +17,10 @@ export const alignElements = ( selectedElements, elementsMap, ); - const selectionBoundingBox = getCommonBoundingBox(selectedElements); + const selectionBounds = getCommonBounds(selectedElements); return groups.flatMap((group) => { - const translation = calculateTranslation( - group, - selectionBoundingBox, - alignment, - ); + const translation = calculateTranslation(group, selectionBounds, alignment); return group.map((element) => newElementWith(element, { x: element.x + translation.x, @@ -37,10 +32,30 @@ export const alignElements = ( const calculateTranslation = ( group: ExcalidrawElement[], - selectionBoundingBox: BoundingBox, + selectionBounds: Bounds, { axis, position }: Alignment, ): { x: number; y: number } => { - const groupBoundingBox = getCommonBoundingBox(group); + const selectionBoundingBox = { + minX: selectionBounds[0], + minY: selectionBounds[1], + maxX: selectionBounds[2], + maxY: selectionBounds[3], + midX: (selectionBounds[0] + selectionBounds[2]) / 2, + midY: (selectionBounds[1] + selectionBounds[3]) / 2, + width: selectionBounds[2] - selectionBounds[0], + height: selectionBounds[3] - selectionBounds[1], + }; + const groupBounds = getCommonBounds(group); + const groupBoundingBox = { + minX: groupBounds[0], + minY: groupBounds[1], + maxX: groupBounds[2], + maxY: groupBounds[3], + midX: (groupBounds[0] + groupBounds[2]) / 2, + midY: (groupBounds[1] + groupBounds[3]) / 2, + width: groupBounds[2] - groupBounds[0], + height: groupBounds[3] - groupBounds[1], + }; const [min, max]: ["minX" | "minY", "maxX" | "maxY"] = axis === "x" ? ["minX", "maxX"] : ["minY", "maxY"]; diff --git a/packages/excalidraw/data/library.ts b/packages/excalidraw/data/library.ts index fe5eb8a557..c14d933816 100644 --- a/packages/excalidraw/data/library.ts +++ b/packages/excalidraw/data/library.ts @@ -11,7 +11,6 @@ import type App from "../components/App"; import { atom } from "jotai"; import { jotaiStore } from "../jotai"; import type { ExcalidrawElement } from "../element/types"; -import { getCommonBoundingBox } from "../element/bounds"; import { AbortError } from "../errors"; import { t } from "../i18n"; import { useEffect, useRef } from "react"; @@ -34,7 +33,7 @@ import { import type { MaybePromise } from "../utility-types"; import { Emitter } from "../emitter"; import { Queue } from "../queue"; -import { hashElementsVersion, hashString } from "../element"; +import { getCommonBounds, hashElementsVersion, hashString } from "../element"; type LibraryUpdate = { /** deleted library items since last onLibraryChange event */ @@ -387,7 +386,8 @@ export const distributeLibraryItemsOnSquareGrid = ( const maxHeight = libraryItems .slice(row * ITEMS_PER_ROW, row * ITEMS_PER_ROW + ITEMS_PER_ROW) .reduce((acc, item) => { - const { height } = getCommonBoundingBox(item.elements); + const bounds = getCommonBounds(item.elements); + const height = bounds[3] - bounds[1]; return Math.max(acc, height); }, 0); return maxHeight; @@ -402,7 +402,9 @@ export const distributeLibraryItemsOnSquareGrid = ( currCol = 0; } if (currCol === targetCol) { - const { width } = getCommonBoundingBox(item.elements); + const bounds = getCommonBounds(item.elements); + const width = bounds[2] - bounds[0]; + maxWidth = Math.max(maxWidth, width); } index++; @@ -434,7 +436,10 @@ export const distributeLibraryItemsOnSquareGrid = ( } maxWidthCurrCol = getMaxWidthPerCol(col); - const { minX, minY, width, height } = getCommonBoundingBox(item.elements); + const bounds = getCommonBounds(item.elements); + const [minX, minY, maxX, maxY] = bounds; + const width = maxX - minX; + const height = maxY - minY; const offsetCenterX = (maxWidthCurrCol - width) / 2; const offsetCenterY = (maxHeightCurrRow - height) / 2; resElements.push( diff --git a/packages/excalidraw/distribute.ts b/packages/excalidraw/distribute.ts index 368b2f24da..0118b52209 100644 --- a/packages/excalidraw/distribute.ts +++ b/packages/excalidraw/distribute.ts @@ -1,7 +1,7 @@ import { newElementWith } from "./element/mutateElement"; import { getMaximumGroups } from "./groups"; -import { getCommonBoundingBox } from "./element/bounds"; import type { ElementsMap, ExcalidrawElement } from "./element/types"; +import { getCommonBounds } from "./element/bounds"; export interface Distribution { space: "between"; @@ -18,9 +18,35 @@ export const distributeElements = ( ? (["minX", "midX", "maxX", "width"] as const) : (["minY", "midY", "maxY", "height"] as const); - const bounds = getCommonBoundingBox(selectedElements); + const bb = getCommonBounds(selectedElements); + const bounds = { + minX: bb[0], + minY: bb[1], + maxX: bb[2], + maxY: bb[3], + midX: (bb[0] + bb[2]) / 2, + midY: (bb[1] + bb[3]) / 2, + width: bb[2] - bb[0], + height: bb[3] - bb[1], + }; const groups = getMaximumGroups(selectedElements, elementsMap) - .map((group) => [group, getCommonBoundingBox(group)] as const) + .map((group) => { + const bounds = getCommonBounds(group); + + return [ + group, + { + minX: bounds[0], + minY: bounds[1], + maxX: bounds[2], + maxY: bounds[3], + midX: (bounds[0] + bounds[2]) / 2, + midY: (bounds[1] + bounds[3]) / 2, + width: bounds[2] - bounds[0], + height: bounds[3] - bounds[1], + }, + ] as const; + }) .sort((a, b) => a[1][mid] - b[1][mid]); let span = 0; diff --git a/packages/excalidraw/element/bounds.ts b/packages/excalidraw/element/bounds.ts index 5593219328..b39f965f52 100644 --- a/packages/excalidraw/element/bounds.ts +++ b/packages/excalidraw/element/bounds.ts @@ -2,7 +2,6 @@ import type { ExcalidrawElement, ExcalidrawLinearElement, ExcalidrawFreeDrawElement, - NonDeleted, ExcalidrawTextElementWithContainer, ElementsMap, } from "./types"; @@ -643,33 +642,6 @@ export const getClosestElementBounds = ( return getElementBounds(closestElement, elementsMap); }; -export interface BoundingBox { - minX: number; - minY: number; - maxX: number; - maxY: number; - midX: number; - midY: number; - width: number; - height: number; -} - -export const getCommonBoundingBox = ( - elements: ExcalidrawElement[] | readonly NonDeleted[], -): BoundingBox => { - const [minX, minY, maxX, maxY] = getCommonBounds(elements); - return { - minX, - minY, - maxX, - maxY, - width: maxX - minX, - height: maxY - minY, - midX: (minX + maxX) / 2, - midY: (minY + maxY) / 2, - }; -}; - /** * returns scene coords of user's editor viewport (visible canvas area) bounds */ diff --git a/packages/excalidraw/element/resizeElements.ts b/packages/excalidraw/element/resizeElements.ts index 0704740c85..0191fddab6 100644 --- a/packages/excalidraw/element/resizeElements.ts +++ b/packages/excalidraw/element/resizeElements.ts @@ -17,7 +17,6 @@ import { getElementAbsoluteCoords, getCommonBounds, getResizedElementAbsoluteCoords, - getCommonBoundingBox, } from "./bounds"; import { isArrowElement, @@ -808,9 +807,11 @@ export const resizeMultipleElements = ( return [...acc, { ...text, ...xy }]; }, [] as ExcalidrawTextElementWithContainer[]); - const { minX, minY, maxX, maxY, midX, midY } = getCommonBoundingBox( + const [minX, minY, maxX, maxY] = getCommonBounds( targetElements.map(({ orig }) => orig).concat(boundTextElements), ); + const midX = (minX + maxX) / 2; + const midY = (minY + maxY) / 2; const width = maxX - minX; const height = maxY - minY; diff --git a/packages/excalidraw/tests/library.test.tsx b/packages/excalidraw/tests/library.test.tsx index 51b139ce37..10ef75e185 100644 --- a/packages/excalidraw/tests/library.test.tsx +++ b/packages/excalidraw/tests/library.test.tsx @@ -3,7 +3,7 @@ import { vi } from "vitest"; import { fireEvent, render, waitFor } from "./test-utils"; import { act, queryByTestId } from "@testing-library/react"; -import { Excalidraw } from "../index"; +import { Excalidraw, getCommonBounds } from "../index"; import { API } from "./helpers/api"; import { MIME_TYPES } from "../constants"; import type { LibraryItem, LibraryItems } from "../types"; @@ -11,7 +11,6 @@ import { UI } from "./helpers/ui"; import { serializeLibraryAsJSON } from "../data/json"; import { distributeLibraryItemsOnSquareGrid } from "../data/library"; import type { ExcalidrawGenericElement } from "../element/types"; -import { getCommonBoundingBox } from "../element/bounds"; import { parseLibraryJSON } from "../data/blob"; const { h } = window; @@ -294,6 +293,7 @@ describe("distributeLibraryItemsOnSquareGrid()", () => { expect(distributed.length).toEqual( libraryItems.map((x) => x.elements).flat().length, ); + const el4_el5_bounds = getCommonBounds([el4, el5]); expect(distributed).toEqual( expect.arrayContaining([ expect.objectContaining({ @@ -306,7 +306,7 @@ describe("distributeLibraryItemsOnSquareGrid()", () => { x: el1.width + PADDING + - (getCommonBoundingBox([el4, el5]).width - el2.width) / 2, + (el4_el5_bounds[2] - el4_el5_bounds[0] - el2.width) / 2, y: Math.abs(el1.height - el2.height) / 2, }), expect.objectContaining({ diff --git a/packages/excalidraw/visualdebug.ts b/packages/excalidraw/visualdebug.ts index 2c5587c597..f6cb1d3c87 100644 --- a/packages/excalidraw/visualdebug.ts +++ b/packages/excalidraw/visualdebug.ts @@ -1,6 +1,6 @@ import type { LineSegment } from "../math"; import { isLineSegment, lineSegment, point, type GlobalPoint } from "../math"; -import type { BoundingBox, Bounds } from "./element/bounds"; +import type { Bounds } from "./element/bounds"; import { isBounds } from "./element/typeChecks"; // The global data holder to collect the debug operations @@ -72,41 +72,6 @@ export const debugDrawPoint = ( ); }; -export const debugDrawBoundingBox = ( - box: BoundingBox | BoundingBox[], - opts?: { - color?: string; - permanent?: boolean; - }, -) => { - (Array.isArray(box) ? box : [box]).forEach((bbox) => - debugDrawLine( - [ - lineSegment( - point(bbox.minX, bbox.minY), - point(bbox.maxX, bbox.minY), - ), - lineSegment( - point(bbox.maxX, bbox.minY), - point(bbox.maxX, bbox.maxY), - ), - lineSegment( - point(bbox.maxX, bbox.maxY), - point(bbox.minX, bbox.maxY), - ), - lineSegment( - point(bbox.minX, bbox.maxY), - point(bbox.minX, bbox.minY), - ), - ], - { - color: opts?.color ?? "cyan", - permanent: opts?.permanent, - }, - ), - ); -}; - export const debugDrawBounds = ( box: Bounds | Bounds[], opts?: { diff --git a/packages/utils/index.ts b/packages/utils/index.ts index a51b3c9cc3..e2e1eee7f8 100644 --- a/packages/utils/index.ts +++ b/packages/utils/index.ts @@ -1,3 +1,2 @@ export * from "./export"; export * from "./withinBounds"; -export { getCommonBounds } from "../excalidraw/element/bounds";