Pārlūkot izejas kodu

fix: collision regressions from vector geometry rewrite (#7902)

Ryan Di 1 gadu atpakaļ
vecāks
revīzija
bbcca06b94

+ 16 - 8
packages/excalidraw/components/App.tsx

@@ -230,6 +230,7 @@ import {
   getEllipseShape,
   getFreedrawShape,
   getPolygonShape,
+  getSelectionBoxShape,
 } from "../../utils/geometry/shape";
 import { isPointInShape } from "../../utils/collision";
 import {
@@ -416,7 +417,6 @@ import { withBatchedUpdates, withBatchedUpdatesThrottled } from "../reactUtils";
 import { getRenderOpacity } from "../renderer/renderElement";
 import {
   hitElementBoundText,
-  hitElementBoundingBox,
   hitElementBoundingBoxOnly,
   hitElementItself,
   shouldTestInside,
@@ -4462,10 +4462,18 @@ class App extends React.Component<AppProps, AppState> {
 
       // If we're hitting element with highest z-index only on its bounding box
       // while also hitting other element figure, the latter should be considered.
-      return isPointInShape(
-        [x, y],
-        this.getElementShape(elementWithHighestZIndex),
-      )
+      return hitElementItself({
+        x,
+        y,
+        element: elementWithHighestZIndex,
+        shape: this.getElementShape(elementWithHighestZIndex),
+        // when overlapping, we would like to be more precise
+        // this also avoids the need to update past tests
+        threshold: this.getHitThreshold() / 2,
+        frameNameBound: isFrameLikeElement(elementWithHighestZIndex)
+          ? this.frameNameBoundsCache.get(elementWithHighestZIndex)
+          : null,
+      })
         ? elementWithHighestZIndex
         : allHitElements[allHitElements.length - 2];
     }
@@ -4540,13 +4548,13 @@ class App extends React.Component<AppProps, AppState> {
       this.state.selectedElementIds[element.id] &&
       shouldShowBoundingBox([element], this.state)
     ) {
-      return hitElementBoundingBox(
-        x,
-        y,
+      const selectionShape = getSelectionBoxShape(
         element,
         this.scene.getNonDeletedElementsMap(),
         this.getHitThreshold(),
       );
+
+      return isPointInShape([x, y], selectionShape);
     }
 
     // take bound text element into consideration for hit collision as well

+ 33 - 0
packages/utils/geometry/shape.ts

@@ -12,8 +12,11 @@
  * to pure shapes
  */
 
+import { getElementAbsoluteCoords } from "../../excalidraw/element";
 import {
+  ElementsMap,
   ExcalidrawDiamondElement,
+  ExcalidrawElement,
   ExcalidrawEllipseElement,
   ExcalidrawEmbeddableElement,
   ExcalidrawFrameLikeElement,
@@ -133,6 +136,36 @@ export const getPolygonShape = (
   };
 };
 
+// return the selection box for an element, possibly rotated as well
+export const getSelectionBoxShape = (
+  element: ExcalidrawElement,
+  elementsMap: ElementsMap,
+  padding = 10,
+) => {
+  let [x1, y1, x2, y2, cx, cy] = getElementAbsoluteCoords(
+    element,
+    elementsMap,
+    true,
+  );
+
+  x1 -= padding;
+  x2 += padding;
+  y1 -= padding;
+  y2 += padding;
+
+  const angleInDegrees = angleToDegrees(element.angle);
+  const center: Point = [cx, cy];
+  const topLeft = pointRotate([x1, y1], angleInDegrees, center);
+  const topRight = pointRotate([x2, y1], angleInDegrees, center);
+  const bottomLeft = pointRotate([x1, y2], angleInDegrees, center);
+  const bottomRight = pointRotate([x2, y2], angleInDegrees, center);
+
+  return {
+    type: "polygon",
+    data: [topLeft, topRight, bottomRight, bottomLeft],
+  } as GeometricShape;
+};
+
 // ellipse
 export const getEllipseShape = (
   element: ExcalidrawEllipseElement,