diff options
| author | kj_sh604 | 2026-03-15 16:19:35 -0400 |
|---|---|---|
| committer | kj_sh604 | 2026-03-15 16:19:35 -0400 |
| commit | 6ec259a0e71174651bae95d4628138bf6fd68742 (patch) | |
| tree | 5e33c6a5ec091ecabfcb257fdc7b6a88ed8754ac /packages/excalidraw/distribute.ts | |
| parent | 16c8578b15c727f22921f8a80a56ee4d4e7f2272 (diff) | |
refactor: packages/
Diffstat (limited to 'packages/excalidraw/distribute.ts')
| -rw-r--r-- | packages/excalidraw/distribute.ts | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/packages/excalidraw/distribute.ts b/packages/excalidraw/distribute.ts new file mode 100644 index 0000000..368b2f2 --- /dev/null +++ b/packages/excalidraw/distribute.ts @@ -0,0 +1,91 @@ +import { newElementWith } from "./element/mutateElement"; +import { getMaximumGroups } from "./groups"; +import { getCommonBoundingBox } from "./element/bounds"; +import type { ElementsMap, ExcalidrawElement } from "./element/types"; + +export interface Distribution { + space: "between"; + axis: "x" | "y"; +} + +export const distributeElements = ( + selectedElements: ExcalidrawElement[], + elementsMap: ElementsMap, + distribution: Distribution, +): ExcalidrawElement[] => { + const [start, mid, end, extent] = + distribution.axis === "x" + ? (["minX", "midX", "maxX", "width"] as const) + : (["minY", "midY", "maxY", "height"] as const); + + const bounds = getCommonBoundingBox(selectedElements); + const groups = getMaximumGroups(selectedElements, elementsMap) + .map((group) => [group, getCommonBoundingBox(group)] as const) + .sort((a, b) => a[1][mid] - b[1][mid]); + + let span = 0; + for (const group of groups) { + span += group[1][extent]; + } + + const step = (bounds[extent] - span) / (groups.length - 1); + + if (step < 0) { + // If we have a negative step, we'll need to distribute from centers + // rather than from gaps. Buckle up, this is a weird one. + + // Get indices of boxes that define start and end of our bounding box + const index0 = groups.findIndex((g) => g[1][start] === bounds[start]); + const index1 = groups.findIndex((g) => g[1][end] === bounds[end]); + + // Get our step, based on the distance between the center points of our + // start and end boxes + const step = + (groups[index1][1][mid] - groups[index0][1][mid]) / (groups.length - 1); + + let pos = groups[index0][1][mid]; + + return groups.flatMap(([group, box], index) => { + const translation = { + x: 0, + y: 0, + }; + + // Don't move our start and end boxes + if (index !== index0 && index !== index1) { + pos += step; + translation[distribution.axis] = pos - box[mid]; + } + + return group.map((element) => + newElementWith(element, { + x: element.x + translation.x, + y: element.y + translation.y, + }), + ); + }); + } + + // Distribute from gaps + + let pos = bounds[start]; + + return groups.flatMap(([group, box]) => { + const translation = { + x: 0, + y: 0, + }; + + translation[distribution.axis] = pos - box[start]; + + pos += step; + pos += box[extent]; + + return group.map((element) => + newElementWith(element, { + x: element.x + translation.x, + y: element.y + translation.y, + }), + ); + }); +}; |
