You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

distribute.ts 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import Command from "./command"
  2. import history from "../history"
  3. import { AlignType, Data, DistributeType } from "types"
  4. import * as vec from "utils/vec"
  5. import {
  6. getBoundsCenter,
  7. getBoundsFromPoints,
  8. getCommonBounds,
  9. getPage,
  10. getSelectedShapes,
  11. } from "utils/utils"
  12. import { getShapeUtils } from "lib/shape-utils"
  13. export default function distributeCommand(data: Data, type: DistributeType) {
  14. const { currentPageId } = data
  15. const selectedShapes = getSelectedShapes(data)
  16. const entries = selectedShapes.map(
  17. (shape) => [shape.id, getShapeUtils(shape).getBounds(shape)] as const
  18. )
  19. const boundsForShapes = Object.fromEntries(entries)
  20. const commonBounds = getCommonBounds(...entries.map((entry) => entry[1]))
  21. const innerBounds = getBoundsFromPoints(
  22. entries.map((entry) => getBoundsCenter(entry[1]))
  23. )
  24. const midX = commonBounds.minX + commonBounds.width / 2
  25. const midY = commonBounds.minY + commonBounds.height / 2
  26. const centers = Object.fromEntries(
  27. selectedShapes.map((shape) => [
  28. shape.id,
  29. getBoundsCenter(boundsForShapes[shape.id]),
  30. ])
  31. )
  32. history.execute(
  33. data,
  34. new Command({
  35. name: "distributed",
  36. category: "canvas",
  37. do(data) {
  38. const { shapes } = getPage(data, currentPageId)
  39. const len = entries.length
  40. switch (type) {
  41. case DistributeType.Horizontal: {
  42. const span = entries.reduce((a, c) => a + c[1].width, 0)
  43. if (span > commonBounds.width) {
  44. const left = entries.sort((a, b) => a[1].minX - b[1].minX)[0]
  45. const right = entries.sort((a, b) => b[1].maxX - a[1].maxX)[0]
  46. const entriesToMove = entries
  47. .filter((a) => a !== left && a !== right)
  48. .sort((a, b) => centers[a[0]][0] - centers[b[0]][0])
  49. const step =
  50. (centers[right[0]][0] - centers[left[0]][0]) / (len - 1)
  51. const x = centers[left[0]][0] + step
  52. for (let i = 0; i < entriesToMove.length; i++) {
  53. const [id, bounds] = entriesToMove[i]
  54. const shape = shapes[id]
  55. getShapeUtils(shape).translateTo(shape, [
  56. x + step * i - bounds.width / 2,
  57. bounds.minY,
  58. ])
  59. }
  60. } else {
  61. const sortedByCenter = entries.sort(
  62. (a, b) => centers[a[0]][0] - centers[b[0]][0]
  63. )
  64. let x = commonBounds.minX
  65. const step = (commonBounds.width - span) / (len - 1)
  66. for (let i = 0; i < sortedByCenter.length - 1; i++) {
  67. const [id, bounds] = sortedByCenter[i]
  68. const shape = shapes[id]
  69. getShapeUtils(shape).translateTo(shape, [x, bounds.minY])
  70. x += bounds.width + step
  71. }
  72. }
  73. break
  74. }
  75. case DistributeType.Vertical: {
  76. const span = entries.reduce((a, c) => a + c[1].height, 0)
  77. if (span > commonBounds.height) {
  78. const top = entries.sort((a, b) => a[1].minY - b[1].minY)[0]
  79. const bottom = entries.sort((a, b) => b[1].maxY - a[1].maxY)[0]
  80. const entriesToMove = entries
  81. .filter((a) => a !== top && a !== bottom)
  82. .sort((a, b) => centers[a[0]][1] - centers[b[0]][1])
  83. const step =
  84. (centers[bottom[0]][1] - centers[top[0]][1]) / (len - 1)
  85. const y = centers[top[0]][1] + step
  86. for (let i = 0; i < entriesToMove.length; i++) {
  87. const [id, bounds] = entriesToMove[i]
  88. const shape = shapes[id]
  89. getShapeUtils(shape).translateTo(shape, [
  90. bounds.minX,
  91. y + step * i - bounds.height / 2,
  92. ])
  93. }
  94. } else {
  95. const sortedByCenter = entries.sort(
  96. (a, b) => centers[a[0]][1] - centers[b[0]][1]
  97. )
  98. let y = commonBounds.minY
  99. const step = (commonBounds.height - span) / (len - 1)
  100. for (let i = 0; i < sortedByCenter.length - 1; i++) {
  101. const [id, bounds] = sortedByCenter[i]
  102. const shape = shapes[id]
  103. getShapeUtils(shape).translateTo(shape, [bounds.minX, y])
  104. y += bounds.height + step
  105. }
  106. }
  107. break
  108. }
  109. }
  110. },
  111. undo(data) {
  112. const { shapes } = getPage(data, currentPageId)
  113. for (let id in boundsForShapes) {
  114. const shape = shapes[id]
  115. const initialBounds = boundsForShapes[id]
  116. getShapeUtils(shape).translateTo(shape, [
  117. initialBounds.minX,
  118. initialBounds.minY,
  119. ])
  120. }
  121. },
  122. })
  123. )
  124. }