sizeHelpers.ts 2.7 KB

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