Kaynağa Gözat

chore: Element center point util (#9298)

jhanma17dev 4 ay önce
ebeveyn
işleme
dff69e9191

+ 16 - 1
packages/common/src/utils.ts

@@ -1,9 +1,10 @@
-import { average } from "@excalidraw/math";
+import { average, pointFrom, type GlobalPoint } from "@excalidraw/math";
 
 import type {
   ExcalidrawBindableElement,
   FontFamilyValues,
   FontString,
+  ExcalidrawElement,
 } from "@excalidraw/element/types";
 
 import type {
@@ -1201,3 +1202,17 @@ export const escapeDoubleQuotes = (str: string) => {
 
 export const castArray = <T>(value: T | T[]): T[] =>
   Array.isArray(value) ? value : [value];
+
+export const elementCenterPoint = (
+  element: ExcalidrawElement,
+  xOffset: number = 0,
+  yOffset: number = 0,
+) => {
+  const { x, y, width, height } = element;
+
+  const centerXPoint = x + width / 2 + xOffset;
+
+  const centerYPoint = y + height / 2 + yOffset;
+
+  return pointFrom<GlobalPoint>(centerXPoint, centerYPoint);
+};

+ 11 - 35
packages/element/src/binding.ts

@@ -6,6 +6,7 @@ import {
   invariant,
   isDevEnv,
   isTestEnv,
+  elementCenterPoint,
 } from "@excalidraw/common";
 
 import {
@@ -904,13 +905,7 @@ export const getHeadingForElbowArrowSnap = (
 
   if (!distance) {
     return vectorToHeading(
-      vectorFromPoint(
-        p,
-        pointFrom<GlobalPoint>(
-          bindableElement.x + bindableElement.width / 2,
-          bindableElement.y + bindableElement.height / 2,
-        ),
-      ),
+      vectorFromPoint(p, elementCenterPoint(bindableElement)),
     );
   }
 
@@ -1040,10 +1035,7 @@ export const avoidRectangularCorner = (
   element: ExcalidrawBindableElement,
   p: GlobalPoint,
 ): GlobalPoint => {
-  const center = pointFrom<GlobalPoint>(
-    element.x + element.width / 2,
-    element.y + element.height / 2,
-  );
+  const center = elementCenterPoint(element);
   const nonRotatedPoint = pointRotateRads(p, center, -element.angle as Radians);
 
   if (nonRotatedPoint[0] < element.x && nonRotatedPoint[1] < element.y) {
@@ -1140,10 +1132,9 @@ export const snapToMid = (
   tolerance: number = 0.05,
 ): GlobalPoint => {
   const { x, y, width, height, angle } = element;
-  const center = pointFrom<GlobalPoint>(
-    x + width / 2 - 0.1,
-    y + height / 2 - 0.1,
-  );
+
+  const center = elementCenterPoint(element, -0.1, -0.1);
+
   const nonRotated = pointRotateRads(p, center, -angle as Radians);
 
   // snap-to-center point is adaptive to element size, but we don't want to go
@@ -1228,10 +1219,7 @@ const updateBoundPoint = (
         startOrEnd === "startBinding" ? "start" : "end",
         elementsMap,
       ).fixedPoint;
-    const globalMidPoint = pointFrom<GlobalPoint>(
-      bindableElement.x + bindableElement.width / 2,
-      bindableElement.y + bindableElement.height / 2,
-    );
+    const globalMidPoint = elementCenterPoint(bindableElement);
     const global = pointFrom<GlobalPoint>(
       bindableElement.x + fixedPoint[0] * bindableElement.width,
       bindableElement.y + fixedPoint[1] * bindableElement.height,
@@ -1275,10 +1263,7 @@ const updateBoundPoint = (
         elementsMap,
       );
 
-    const center = pointFrom<GlobalPoint>(
-      bindableElement.x + bindableElement.width / 2,
-      bindableElement.y + bindableElement.height / 2,
-    );
+    const center = elementCenterPoint(bindableElement);
     const interceptorLength =
       pointDistance(adjacentPoint, edgePointAbsolute) +
       pointDistance(adjacentPoint, center) +
@@ -1771,10 +1756,7 @@ const determineFocusDistance = (
   // Another point on the line, in absolute coordinates (closer to element)
   b: GlobalPoint,
 ): number => {
-  const center = pointFrom<GlobalPoint>(
-    element.x + element.width / 2,
-    element.y + element.height / 2,
-  );
+  const center = elementCenterPoint(element);
 
   if (pointsEqual(a, b)) {
     return 0;
@@ -1904,10 +1886,7 @@ const determineFocusPoint = (
   focus: number,
   adjacentPoint: GlobalPoint,
 ): GlobalPoint => {
-  const center = pointFrom<GlobalPoint>(
-    element.x + element.width / 2,
-    element.y + element.height / 2,
-  );
+  const center = elementCenterPoint(element);
 
   if (focus === 0) {
     return center;
@@ -2338,10 +2317,7 @@ export const getGlobalFixedPointForBindableElement = (
       element.x + element.width * fixedX,
       element.y + element.height * fixedY,
     ),
-    pointFrom<GlobalPoint>(
-      element.x + element.width / 2,
-      element.y + element.height / 2,
-    ),
+    elementCenterPoint(element),
     element.angle,
   );
 };

+ 5 - 16
packages/element/src/collision.ts

@@ -1,4 +1,4 @@
-import { isTransparent } from "@excalidraw/common";
+import { isTransparent, elementCenterPoint } from "@excalidraw/common";
 import {
   curveIntersectLineSegment,
   isPointWithinBounds,
@@ -16,7 +16,7 @@ import {
 } from "@excalidraw/math/ellipse";
 
 import { isPointInShape, isPointOnShape } from "@excalidraw/utils/collision";
-import { getPolygonShape } from "@excalidraw/utils/shape";
+import { type GeometricShape, getPolygonShape } from "@excalidraw/utils/shape";
 
 import type {
   GlobalPoint,
@@ -26,8 +26,6 @@ import type {
   Radians,
 } from "@excalidraw/math";
 
-import type { GeometricShape } from "@excalidraw/utils/shape";
-
 import type { FrameNameBounds } from "@excalidraw/excalidraw/types";
 
 import { getBoundTextShape, isPathALoop } from "./shapes";
@@ -191,10 +189,7 @@ const intersectRectanguloidWithLineSegment = (
   l: LineSegment<GlobalPoint>,
   offset: number = 0,
 ): GlobalPoint[] => {
-  const center = pointFrom<GlobalPoint>(
-    element.x + element.width / 2,
-    element.y + element.height / 2,
-  );
+  const center = elementCenterPoint(element);
   // To emulate a rotated rectangle we rotate the point in the inverse angle
   // instead. It's all the same distance-wise.
   const rotatedA = pointRotateRads<GlobalPoint>(
@@ -253,10 +248,7 @@ const intersectDiamondWithLineSegment = (
   l: LineSegment<GlobalPoint>,
   offset: number = 0,
 ): GlobalPoint[] => {
-  const center = pointFrom<GlobalPoint>(
-    element.x + element.width / 2,
-    element.y + element.height / 2,
-  );
+  const center = elementCenterPoint(element);
 
   // Rotate the point to the inverse direction to simulate the rotated diamond
   // points. It's all the same distance-wise.
@@ -304,10 +296,7 @@ const intersectEllipseWithLineSegment = (
   l: LineSegment<GlobalPoint>,
   offset: number = 0,
 ): GlobalPoint[] => {
-  const center = pointFrom<GlobalPoint>(
-    element.x + element.width / 2,
-    element.y + element.height / 2,
-  );
+  const center = elementCenterPoint(element);
 
   const rotatedA = pointRotateRads(l[0], center, -element.angle as Radians);
   const rotatedB = pointRotateRads(l[1], center, -element.angle as Radians);

+ 3 - 1
packages/element/src/cropElement.ts

@@ -14,6 +14,8 @@ import {
 } from "@excalidraw/math";
 import { type Point } from "points-on-curve";
 
+import { elementCenterPoint } from "@excalidraw/common";
+
 import {
   getElementAbsoluteCoords,
   getResizedElementAbsoluteCoords,
@@ -61,7 +63,7 @@ export const cropElement = (
 
   const rotatedPointer = pointRotateRads(
     pointFrom(pointerX, pointerY),
-    pointFrom(element.x + element.width / 2, element.y + element.height / 2),
+    elementCenterPoint(element),
     -element.angle as Radians,
   );
 

+ 5 - 13
packages/element/src/distance.ts

@@ -1,12 +1,13 @@
 import {
   curvePointDistance,
   distanceToLineSegment,
-  pointFrom,
   pointRotateRads,
 } from "@excalidraw/math";
 
 import { ellipse, ellipseDistanceFromPoint } from "@excalidraw/math/ellipse";
 
+import { elementCenterPoint } from "@excalidraw/common";
+
 import type { GlobalPoint, Radians } from "@excalidraw/math";
 
 import {
@@ -53,10 +54,7 @@ const distanceToRectanguloidElement = (
   element: ExcalidrawRectanguloidElement,
   p: GlobalPoint,
 ) => {
-  const center = pointFrom<GlobalPoint>(
-    element.x + element.width / 2,
-    element.y + element.height / 2,
-  );
+  const center = elementCenterPoint(element);
   // To emulate a rotated rectangle we rotate the point in the inverse angle
   // instead. It's all the same distance-wise.
   const rotatedPoint = pointRotateRads(p, center, -element.angle as Radians);
@@ -84,10 +82,7 @@ const distanceToDiamondElement = (
   element: ExcalidrawDiamondElement,
   p: GlobalPoint,
 ): number => {
-  const center = pointFrom<GlobalPoint>(
-    element.x + element.width / 2,
-    element.y + element.height / 2,
-  );
+  const center = elementCenterPoint(element);
 
   // Rotate the point to the inverse direction to simulate the rotated diamond
   // points. It's all the same distance-wise.
@@ -115,10 +110,7 @@ const distanceToEllipseElement = (
   element: ExcalidrawEllipseElement,
   p: GlobalPoint,
 ): number => {
-  const center = pointFrom(
-    element.x + element.width / 2,
-    element.y + element.height / 2,
-  );
+  const center = elementCenterPoint(element);
   return ellipseDistanceFromPoint(
     // Instead of rotating the ellipse, rotate the point to the inverse angle
     pointRotateRads(p, center, -element.angle as Radians),

+ 2 - 1
packages/element/src/shapes.ts

@@ -4,6 +4,7 @@ import {
   LINE_CONFIRM_THRESHOLD,
   ROUNDNESS,
   invariant,
+  elementCenterPoint,
 } from "@excalidraw/common";
 import {
   isPoint,
@@ -297,7 +298,7 @@ export const aabbForElement = (
     midY: element.y + element.height / 2,
   };
 
-  const center = pointFrom(bbox.midX, bbox.midY);
+  const center = elementCenterPoint(element);
   const [topLeftX, topLeftY] = pointRotateRads(
     pointFrom(bbox.minX, bbox.minY),
     center,

+ 4 - 8
packages/element/src/utils.ts

@@ -10,6 +10,8 @@ import {
   type GlobalPoint,
 } from "@excalidraw/math";
 
+import { elementCenterPoint } from "@excalidraw/common";
+
 import type { Curve, LineSegment } from "@excalidraw/math";
 
 import { getCornerRadius } from "./shapes";
@@ -68,10 +70,7 @@ export function deconstructRectanguloidElement(
     return [sides, []];
   }
 
-  const center = pointFrom<GlobalPoint>(
-    element.x + element.width / 2,
-    element.y + element.height / 2,
-  );
+  const center = elementCenterPoint(element);
 
   const r = rectangle(
     pointFrom(element.x, element.y),
@@ -254,10 +253,7 @@ export function deconstructDiamondElement(
     return [[topRight, bottomRight, bottomLeft, topLeft], []];
   }
 
-  const center = pointFrom<GlobalPoint>(
-    element.x + element.width / 2,
-    element.y + element.height / 2,
-  );
+  const center = elementCenterPoint(element);
 
   const [top, right, bottom, left]: GlobalPoint[] = [
     pointFrom(element.x + topX, element.y + topY),

+ 3 - 3
packages/excalidraw/tests/helpers/ui.ts

@@ -20,7 +20,7 @@ import {
   isTextElement,
   isFrameLikeElement,
 } from "@excalidraw/element/typeChecks";
-import { KEYS, arrayToMap } from "@excalidraw/common";
+import { KEYS, arrayToMap, elementCenterPoint } from "@excalidraw/common";
 
 import type { GlobalPoint, LocalPoint, Radians } from "@excalidraw/math";
 
@@ -151,7 +151,7 @@ export class Keyboard {
 const getElementPointForSelection = (
   element: ExcalidrawElement,
 ): GlobalPoint => {
-  const { x, y, width, height, angle } = element;
+  const { x, y, width, angle } = element;
   const target = pointFrom<GlobalPoint>(
     x +
       (isLinearElement(element) || isFreeDrawElement(element) ? 0 : width / 2),
@@ -166,7 +166,7 @@ const getElementPointForSelection = (
       (bounds[1] + bounds[3]) / 2,
     );
   } else {
-    center = pointFrom(x + width / 2, y + height / 2);
+    center = elementCenterPoint(element);
   }
 
   if (isTextElement(element)) {