tldraw/state/commands/distribute.ts

159 lines
4.9 KiB
TypeScript
Raw Normal View History

2021-05-26 19:20:52 +00:00
import Command from "./command"
import history from "../history"
import { AlignType, Data, DistributeType } from "types"
2021-05-26 21:47:46 +00:00
import * as vec from "utils/vec"
import {
getBoundsCenter,
getBoundsFromPoints,
getCommonBounds,
getPage,
getSelectedShapes,
} from "utils/utils"
2021-05-26 19:20:52 +00:00
import { getShapeUtils } from "lib/shape-utils"
export default function distributeCommand(data: Data, type: DistributeType) {
const { currentPageId } = data
2021-05-26 21:47:46 +00:00
const selectedShapes = getSelectedShapes(data)
const entries = selectedShapes.map(
(shape) => [shape.id, getShapeUtils(shape).getBounds(shape)] as const
)
const boundsForShapes = Object.fromEntries(entries)
const commonBounds = getCommonBounds(...entries.map((entry) => entry[1]))
const innerBounds = getBoundsFromPoints(
entries.map((entry) => getBoundsCenter(entry[1]))
)
const midX = commonBounds.minX + commonBounds.width / 2
const midY = commonBounds.minY + commonBounds.height / 2
const centers = Object.fromEntries(
selectedShapes.map((shape) => [
shape.id,
getBoundsCenter(boundsForShapes[shape.id]),
2021-05-26 19:20:52 +00:00
])
)
history.execute(
data,
new Command({
name: "distributed",
category: "canvas",
do(data) {
const { shapes } = getPage(data, currentPageId)
2021-05-26 21:47:46 +00:00
const len = entries.length
2021-05-26 19:20:52 +00:00
switch (type) {
case DistributeType.Horizontal: {
2021-05-26 21:47:46 +00:00
const sortedByCenter = entries.sort(
([a], [b]) => centers[a][0] - centers[b][0]
)
const span = sortedByCenter.reduce((a, c) => a + c[1].width, 0)
if (span > commonBounds.width) {
const left = sortedByCenter.sort(
(a, b) => a[1].minX - b[1].minX
)[0]
const right = sortedByCenter.sort(
(a, b) => b[1].maxX - a[1].maxX
)[0]
const entriesToMove = sortedByCenter
.filter((a) => a !== left && a !== right)
.sort((a, b) => centers[a[0]][0] - centers[b[0]][0])
const step =
(centers[right[0]][0] - centers[left[0]][0]) / (len - 1)
const x = centers[left[0]][0] + step
for (let i = 0; i < entriesToMove.length; i++) {
const [id, bounds] = entriesToMove[i]
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, [
x + step * i - bounds.width / 2,
bounds.minY,
])
}
} else {
const step = (commonBounds.width - span) / (len - 1)
let x = commonBounds.minX
for (let i = 0; i < sortedByCenter.length - 1; i++) {
const [id, bounds] = sortedByCenter[i]
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, [x, bounds.minY])
x += bounds.width + step
}
}
break
2021-05-26 19:20:52 +00:00
}
case DistributeType.Vertical: {
2021-05-26 21:47:46 +00:00
const sortedByCenter = entries.sort(
([a], [b]) => centers[a][1] - centers[b][1]
)
const span = sortedByCenter.reduce((a, c) => a + c[1].height, 0)
if (span > commonBounds.height) {
const top = sortedByCenter.sort(
(a, b) => a[1].minY - b[1].minY
)[0]
const bottom = sortedByCenter.sort(
(a, b) => b[1].maxY - a[1].maxY
)[0]
const entriesToMove = sortedByCenter
.filter((a) => a !== top && a !== bottom)
.sort((a, b) => centers[a[0]][1] - centers[b[0]][1])
const step =
(centers[bottom[0]][1] - centers[top[0]][1]) / (len - 1)
const y = centers[top[0]][1] + step
for (let i = 0; i < entriesToMove.length; i++) {
const [id, bounds] = entriesToMove[i]
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, [
bounds.minX,
y + step * i - bounds.height / 2,
])
}
} else {
const step = (commonBounds.height - span) / (len - 1)
let y = commonBounds.minY
for (let i = 0; i < sortedByCenter.length - 1; i++) {
const [id, bounds] = sortedByCenter[i]
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, [bounds.minX, y])
y += bounds.height + step
}
}
break
2021-05-26 19:20:52 +00:00
}
}
},
undo(data) {
const { shapes } = getPage(data, currentPageId)
2021-05-26 21:47:46 +00:00
for (let id in boundsForShapes) {
2021-05-26 19:20:52 +00:00
const shape = shapes[id]
2021-05-26 21:47:46 +00:00
const initialBounds = boundsForShapes[id]
getShapeUtils(shape).translateTo(shape, [
initialBounds.minX,
initialBounds.minY,
])
2021-05-26 19:20:52 +00:00
}
},
})
)
}