sizeHelpers.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import { ExcalidrawElement } from "./types";
  2. import { mutateElement } from "./mutateElement";
  3. import { isFreeDrawElement, isLinearElement } from "./typeChecks";
  4. import { SHIFT_LOCKING_ANGLE } from "../constants";
  5. import { AppState } from "../types";
  6. export const isInvisiblySmallElement = (
  7. element: ExcalidrawElement,
  8. ): boolean => {
  9. if (isLinearElement(element) || isFreeDrawElement(element)) {
  10. return element.points.length < 2;
  11. }
  12. return element.width === 0 && element.height === 0;
  13. };
  14. /**
  15. * Makes a perfect shape or diagonal/horizontal/vertical line
  16. */
  17. export const getPerfectElementSize = (
  18. elementType: AppState["activeTool"]["type"],
  19. width: number,
  20. height: number,
  21. ): { width: number; height: number } => {
  22. const absWidth = Math.abs(width);
  23. const absHeight = Math.abs(height);
  24. if (
  25. elementType === "line" ||
  26. elementType === "arrow" ||
  27. elementType === "freedraw"
  28. ) {
  29. const lockedAngle =
  30. Math.round(Math.atan(absHeight / absWidth) / SHIFT_LOCKING_ANGLE) *
  31. SHIFT_LOCKING_ANGLE;
  32. if (lockedAngle === 0) {
  33. height = 0;
  34. } else if (lockedAngle === Math.PI / 2) {
  35. width = 0;
  36. } else {
  37. height =
  38. Math.round(absWidth * Math.tan(lockedAngle)) * Math.sign(height) ||
  39. height;
  40. }
  41. } else if (elementType !== "selection") {
  42. height = absWidth * Math.sign(height);
  43. }
  44. return { width, height };
  45. };
  46. export const resizePerfectLineForNWHandler = (
  47. element: ExcalidrawElement,
  48. x: number,
  49. y: number,
  50. ) => {
  51. const anchorX = element.x + element.width;
  52. const anchorY = element.y + element.height;
  53. const distanceToAnchorX = x - anchorX;
  54. const distanceToAnchorY = y - anchorY;
  55. if (Math.abs(distanceToAnchorX) < Math.abs(distanceToAnchorY) / 2) {
  56. mutateElement(element, {
  57. x: anchorX,
  58. width: 0,
  59. y,
  60. height: -distanceToAnchorY,
  61. });
  62. } else if (Math.abs(distanceToAnchorY) < Math.abs(element.width) / 2) {
  63. mutateElement(element, {
  64. y: anchorY,
  65. height: 0,
  66. });
  67. } else {
  68. const nextHeight =
  69. Math.sign(distanceToAnchorY) *
  70. Math.sign(distanceToAnchorX) *
  71. element.width;
  72. mutateElement(element, {
  73. x,
  74. y: anchorY - nextHeight,
  75. width: -distanceToAnchorX,
  76. height: nextHeight,
  77. });
  78. }
  79. };
  80. export const getNormalizedDimensions = (
  81. element: Pick<ExcalidrawElement, "width" | "height" | "x" | "y">,
  82. ): {
  83. width: ExcalidrawElement["width"];
  84. height: ExcalidrawElement["height"];
  85. x: ExcalidrawElement["x"];
  86. y: ExcalidrawElement["y"];
  87. } => {
  88. const ret = {
  89. width: element.width,
  90. height: element.height,
  91. x: element.x,
  92. y: element.y,
  93. };
  94. if (element.width < 0) {
  95. const nextWidth = Math.abs(element.width);
  96. ret.width = nextWidth;
  97. ret.x = element.x - nextWidth;
  98. }
  99. if (element.height < 0) {
  100. const nextHeight = Math.abs(element.height);
  101. ret.height = nextHeight;
  102. ret.y = element.y - nextHeight;
  103. }
  104. return ret;
  105. };