141 lines
4.5 KiB
TypeScript
141 lines
4.5 KiB
TypeScript
import Command from './command'
|
|
import history from '../history'
|
|
import { Data, DistributeType } from 'types'
|
|
import { getBoundsCenter, getCommonBounds } from 'utils'
|
|
import tld from 'utils/tld'
|
|
import { getShapeUtils } from 'state/shape-utils'
|
|
|
|
export default function distributeCommand(
|
|
data: Data,
|
|
type: DistributeType
|
|
): void {
|
|
const selectedShapes = tld
|
|
.getSelectedShapes(data)
|
|
.filter((shape) => !shape.isLocked)
|
|
|
|
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 centers = Object.fromEntries(
|
|
selectedShapes.map((shape) => [
|
|
shape.id,
|
|
getBoundsCenter(boundsForShapes[shape.id]),
|
|
])
|
|
)
|
|
|
|
history.execute(
|
|
data,
|
|
new Command({
|
|
name: 'distribute_shapes',
|
|
category: 'canvas',
|
|
do(data) {
|
|
const { shapes } = tld.getPage(data)
|
|
const len = entries.length
|
|
|
|
switch (type) {
|
|
case DistributeType.Horizontal: {
|
|
const span = entries.reduce((a, c) => a + c[1].width, 0)
|
|
|
|
if (span > commonBounds.width) {
|
|
const left = entries.sort((a, b) => a[1].minX - b[1].minX)[0]
|
|
|
|
const right = entries.sort((a, b) => b[1].maxX - a[1].maxX)[0]
|
|
|
|
const entriesToMove = entries
|
|
.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 entriesToMove = entries.sort(
|
|
(a, b) => centers[a[0]][0] - centers[b[0]][0]
|
|
)
|
|
|
|
let x = commonBounds.minX
|
|
const step = (commonBounds.width - span) / (len - 1)
|
|
|
|
for (let i = 0; i < entriesToMove.length - 1; i++) {
|
|
const [id, bounds] = entriesToMove[i]
|
|
const shape = shapes[id]
|
|
getShapeUtils(shape).translateTo(shape, [x, bounds.minY])
|
|
x += bounds.width + step
|
|
}
|
|
}
|
|
break
|
|
}
|
|
case DistributeType.Vertical: {
|
|
const span = entries.reduce((a, c) => a + c[1].height, 0)
|
|
|
|
if (span > commonBounds.height) {
|
|
const top = entries.sort((a, b) => a[1].minY - b[1].minY)[0]
|
|
|
|
const bottom = entries.sort((a, b) => b[1].maxY - a[1].maxY)[0]
|
|
|
|
const entriesToMove = entries
|
|
.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 entriesToMove = entries.sort(
|
|
(a, b) => centers[a[0]][1] - centers[b[0]][1]
|
|
)
|
|
|
|
let y = commonBounds.minY
|
|
const step = (commonBounds.height - span) / (len - 1)
|
|
|
|
for (let i = 0; i < entriesToMove.length - 1; i++) {
|
|
const [id, bounds] = entriesToMove[i]
|
|
const shape = shapes[id]
|
|
getShapeUtils(shape).translateTo(shape, [bounds.minX, y])
|
|
y += bounds.height + step
|
|
}
|
|
}
|
|
|
|
break
|
|
}
|
|
}
|
|
},
|
|
undo(data) {
|
|
const { shapes } = tld.getPage(data)
|
|
for (const id in boundsForShapes) {
|
|
const shape = shapes[id]
|
|
const initialBounds = boundsForShapes[id]
|
|
getShapeUtils(shape).translateTo(shape, [
|
|
initialBounds.minX,
|
|
initialBounds.minY,
|
|
])
|
|
}
|
|
},
|
|
})
|
|
)
|
|
}
|