123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- import { Bounds, BoundsSnapshot, ShapeBounds } from "types"
-
- export function stretchshapesX(shapes: ShapeBounds[]) {
- const [first, ...rest] = shapes
- let min = first.minX
- let max = first.minX + first.width
- for (let box of rest) {
- min = Math.min(min, box.minX)
- max = Math.max(max, box.minX + box.width)
- }
- return shapes.map((box) => ({ ...box, x: min, width: max - min }))
- }
-
- export function stretchshapesY(shapes: ShapeBounds[]) {
- const [first, ...rest] = shapes
- let min = first.minY
- let max = first.minY + first.height
- for (let box of rest) {
- min = Math.min(min, box.minY)
- max = Math.max(max, box.minY + box.height)
- }
- return shapes.map((box) => ({ ...box, y: min, height: max - min }))
- }
-
- export function distributeshapesX(shapes: ShapeBounds[]) {
- const len = shapes.length
- const sorted = [...shapes].sort((a, b) => a.minX - b.minX)
- let min = sorted[0].minX
-
- sorted.sort((a, b) => a.minX + a.width - b.minX - b.width)
- let last = sorted[len - 1]
- let max = last.minX + last.width
-
- let range = max - min
- let step = range / len
- return sorted.map((box, i) => ({ ...box, x: min + step * i }))
- }
-
- export function distributeshapesY(shapes: ShapeBounds[]) {
- const len = shapes.length
- const sorted = [...shapes].sort((a, b) => a.minY - b.minY)
- let min = sorted[0].minY
-
- sorted.sort((a, b) => a.minY + a.height - b.minY - b.height)
- let last = sorted[len - 1]
- let max = last.minY + last.height
-
- let range = max - min
- let step = range / len
- return sorted.map((box, i) => ({ ...box, y: min + step * i }))
- }
-
- export function alignshapesCenterX(shapes: ShapeBounds[]) {
- let midX = 0
- for (let box of shapes) midX += box.minX + box.width / 2
- midX /= shapes.length
- return shapes.map((box) => ({ ...box, x: midX - box.width / 2 }))
- }
-
- export function alignshapesCenterY(shapes: ShapeBounds[]) {
- let midY = 0
- for (let box of shapes) midY += box.minY + box.height / 2
- midY /= shapes.length
- return shapes.map((box) => ({ ...box, y: midY - box.height / 2 }))
- }
-
- export function alignshapesTop(shapes: ShapeBounds[]) {
- const [first, ...rest] = shapes
- let y = first.minY
- for (let box of rest) if (box.minY < y) y = box.minY
- return shapes.map((box) => ({ ...box, y }))
- }
-
- export function alignshapesBottom(shapes: ShapeBounds[]) {
- const [first, ...rest] = shapes
- let maxY = first.minY + first.height
- for (let box of rest)
- if (box.minY + box.height > maxY) maxY = box.minY + box.height
- return shapes.map((box) => ({ ...box, y: maxY - box.height }))
- }
-
- export function alignshapesLeft(shapes: ShapeBounds[]) {
- const [first, ...rest] = shapes
- let x = first.minX
- for (let box of rest) if (box.minX < x) x = box.minX
- return shapes.map((box) => ({ ...box, x }))
- }
-
- export function alignshapesRight(shapes: ShapeBounds[]) {
- const [first, ...rest] = shapes
- let maxX = first.minX + first.width
- for (let box of rest)
- if (box.minX + box.width > maxX) maxX = box.minX + box.width
- return shapes.map((box) => ({ ...box, x: maxX - box.width }))
- }
-
- // Resizers
-
- export function getBoundingBox(shapes: ShapeBounds[]): Bounds {
- if (shapes.length === 0) {
- return {
- minX: 0,
- minY: 0,
- maxX: 0,
- maxY: 0,
- width: 0,
- height: 0,
- }
- }
-
- const first = shapes[0]
-
- let minX = first.minX
- let minY = first.minY
- let maxX = first.minX + first.width
- let maxY = first.minY + first.height
-
- for (let box of shapes) {
- minX = Math.min(minX, box.minX)
- minY = Math.min(minY, box.minY)
- maxX = Math.max(maxX, box.minX + box.width)
- maxY = Math.max(maxY, box.minY + box.height)
- }
-
- return {
- minX,
- minY,
- maxX,
- maxY,
- width: maxX - minX,
- height: maxY - minY,
- }
- }
-
- export function getSnapshots(
- shapes: ShapeBounds[],
- bounds: Bounds
- ): Record<string, BoundsSnapshot> {
- const acc = {} as Record<string, BoundsSnapshot>
-
- const w = bounds.maxX - bounds.minX
- const h = bounds.maxY - bounds.minY
-
- for (let box of shapes) {
- acc[box.id] = {
- ...box,
- nx: (box.minX - bounds.minX) / w,
- ny: (box.minY - bounds.minY) / h,
- nmx: 1 - (box.minX + box.width - bounds.minX) / w,
- nmy: 1 - (box.minY + box.height - bounds.minY) / h,
- nw: box.width / w,
- nh: box.height / h,
- }
- }
-
- return acc
- }
-
- export function getEdgeResizer(shapes: ShapeBounds[], edge: number) {
- const initial = getBoundingBox(shapes)
- const snapshots = getSnapshots(shapes, initial)
- const mshapes = [...shapes]
-
- let { minX: x0, minY: y0, maxX: x1, maxY: y1 } = initial
- let { minX: mx, minY: my } = initial
- let mw = x1 - x0
- let mh = y1 - y0
-
- return function edgeResize({ x, y }) {
- if (edge === 0 || edge === 2) {
- edge === 0 ? (y0 = y) : (y1 = y)
- my = y0 < y1 ? y0 : y1
- mh = Math.abs(y1 - y0)
- for (let box of mshapes) {
- const { ny, nmy, nh } = snapshots[box.id]
- box.minY = my + (y1 < y0 ? nmy : ny) * mh
- box.height = nh * mh
- }
- } else {
- edge === 1 ? (x1 = x) : (x0 = x)
- mx = x0 < x1 ? x0 : x1
- mw = Math.abs(x1 - x0)
- for (let box of mshapes) {
- const { nx, nmx, nw } = snapshots[box.id]
- box.minX = mx + (x1 < x0 ? nmx : nx) * mw
- box.width = nw * mw
- }
- }
-
- return [
- mshapes,
- {
- x: mx,
- y: my,
- width: mw,
- height: mh,
- maxX: mx + mw,
- maxY: my + mh,
- },
- ]
- }
- }
-
- /**
- * Returns a function that can be used to calculate corner resize transforms.
- * @param shapes An array of the shapes being resized.
- * @param corner A number representing the corner being dragged. Top Left: 0, Top Right: 1, Bottom Right: 2, Bottom Left: 3.
- * @example
- * const resizer = getCornerResizer(selectedshapes, 3)
- * resizer(selectedshapes, )
- */
- export function getCornerResizer(shapes: ShapeBounds[], corner: number) {
- const initial = getBoundingBox(shapes)
- const snapshots = getSnapshots(shapes, initial)
- const mshapes = [...shapes]
-
- let { minX: x0, minY: y0, maxX: x1, maxY: y1 } = initial
- let { minX: mx, minY: my } = initial
- let mw = x1 - x0
- let mh = y1 - y0
-
- return function cornerResizer({ x, y }) {
- corner < 2 ? (y0 = y) : (y1 = y)
- my = y0 < y1 ? y0 : y1
- mh = Math.abs(y1 - y0)
-
- corner === 1 || corner === 2 ? (x1 = x) : (x0 = x)
- mx = x0 < x1 ? x0 : x1
- mw = Math.abs(x1 - x0)
-
- for (let box of mshapes) {
- const { nx, nmx, nw, ny, nmy, nh } = snapshots[box.id]
- box.minX = mx + (x1 < x0 ? nmx : nx) * mw
- box.minY = my + (y1 < y0 ? nmy : ny) * mh
- box.width = nw * mw
- box.height = nh * mh
- }
-
- return [
- mshapes,
- {
- x: mx,
- y: my,
- width: mw,
- height: mh,
- maxX: mx + mw,
- maxY: my + mh,
- },
- ]
- }
- }
|