Jelajahi Sumber

fix: do not snap to each other when moving multiple points together

Ryan Di 8 bulan lalu
induk
melakukan
0e197ef5c4

+ 6 - 11
packages/element/src/linearElementEditor.ts

@@ -370,17 +370,12 @@ export class LinearElementEditor {
         const effectiveGridX = referencePointCoords[0] + dxFromReference;
         const effectiveGridY = referencePointCoords[1] + dyFromReference;
 
-        let newDraggingPointPosition = pointFrom(
-          effectiveGridX,
-          effectiveGridY,
-        );
-
         if (!isElbowArrow(element)) {
           const { snapOffset, snapLines } = snapLinearElementPoint(
             app.scene.getNonDeletedElements(),
             element,
             lastClickedPoint,
-            { x: effectiveGridX, y: effectiveGridY },
+            pointFrom<GlobalPoint>(effectiveGridX, effectiveGridY),
             app,
             event,
             elementsMap,
@@ -448,7 +443,7 @@ export class LinearElementEditor {
           -element.angle as Radians,
         );
 
-        newDraggingPointPosition = pointFrom(
+        const newDraggingPointPosition = pointFrom(
           referencePoint[0] + rotatedX,
           referencePoint[1] + rotatedY,
         );
@@ -477,11 +472,11 @@ export class LinearElementEditor {
           app.scene.getNonDeletedElements(),
           element,
           lastClickedPoint,
-          { x: originalPointerX, y: originalPointerY },
+          pointFrom(originalPointerX, originalPointerY),
           app,
           event,
           elementsMap,
-          { includeSelfPoints: true },
+          { includeSelfPoints: true, selectedPointsIndices },
         );
 
         _snapLines = snapLines;
@@ -1223,7 +1218,7 @@ export class LinearElementEditor {
           app.scene.getNonDeletedElements(),
           element,
           points.length - 1,
-          { x: effectiveGridX, y: effectiveGridY },
+          pointFrom(effectiveGridX, effectiveGridY),
           app,
           event,
           elementsMap,
@@ -1311,7 +1306,7 @@ export class LinearElementEditor {
         app.scene.getNonDeletedElements(),
         element,
         points.length - 1,
-        { x: originalPointerX, y: originalPointerY },
+        pointFrom(originalPointerX, originalPointerY),
         app,
         event,
         elementsMap,

+ 29 - 52
packages/element/src/snapping.ts

@@ -2,7 +2,6 @@ import {
   isCloseTo,
   pointFrom,
   pointRotateRads,
-  pointsEqual,
   rangeInclusive,
   rangeIntersection,
   rangesOverlap,
@@ -198,13 +197,12 @@ export const areRoughlyEqual = (a: number, b: number, precision = 0.01) => {
 
 export const getLinearElementPoints = (
   element: ExcalidrawLinearElement,
-  elementsMap: ElementsMap,
   options: {
     dragOffset?: Vector2D;
-    excludePointIndex?: number;
+    excludePointsIndices?: readonly number[];
   } = {},
 ): GlobalPoint[] => {
-  const { dragOffset, excludePointIndex } = options;
+  const { dragOffset, excludePointsIndices } = options;
 
   if (isElbowArrow(element)) {
     return [];
@@ -226,27 +224,25 @@ export const getLinearElementPoints = (
 
   for (let i = 0; i < element.points.length; i++) {
     // Skip the point being edited if specified
-    if (excludePointIndex !== undefined && i === excludePointIndex) {
+    if (
+      excludePointsIndices?.length &&
+      excludePointsIndices.find((index) => index === i) !== undefined
+    ) {
       continue;
     }
 
-    const localPoint = element.points[i];
-    const globalX = elementX + localPoint[0];
-    const globalY = elementY + localPoint[1];
+    const point = element.points[i];
+    const globalX = elementX + point[0];
+    const globalY = elementY + point[1];
 
-    // Apply rotation if element is rotated
-    if (element.angle !== 0) {
-      const cx = elementX + element.width / 2;
-      const cy = elementY + element.height / 2;
-      const rotated = pointRotateRads<GlobalPoint>(
-        pointFrom(globalX, globalY),
-        pointFrom(cx, cy),
-        element.angle,
-      );
-      globalPoints.push(pointFrom(round(rotated[0]), round(rotated[1])));
-    } else {
-      globalPoints.push(pointFrom(round(globalX), round(globalY)));
-    }
+    const cx = elementX + element.width / 2;
+    const cy = elementY + element.height / 2;
+    const rotated = pointRotateRads<GlobalPoint>(
+      pointFrom(globalX, globalY),
+      pointFrom(cx, cy),
+      element.angle,
+    );
+    globalPoints.push(pointFrom(round(rotated[0]), round(rotated[1])));
   }
 
   return globalPoints;
@@ -296,7 +292,7 @@ export const getElementsCorners = (
       !boundingBoxCorners
     ) {
       // For linear elements, use actual points instead of bounding box
-      const linearPoints = getLinearElementPoints(element, elementsMap, {
+      const linearPoints = getLinearElementPoints(element, {
         dragOffset,
       });
       result = linearPoints;
@@ -714,6 +710,7 @@ export const getReferenceSnapPointsForLinearElementPoint = (
   elementsMap: ElementsMap,
   options: {
     includeSelfPoints?: boolean;
+    selectedPointsIndices?: readonly number[];
   } = {},
 ) => {
   const { includeSelfPoints = false } = options;
@@ -743,24 +740,9 @@ export const getReferenceSnapPointsForLinearElementPoint = (
 
   // Include other points from the same linear element when creating new points or in editing mode
   if (includeSelfPoints) {
-    const elementPoints = getLinearElementPoints(editingElement, elementsMap, {
-      excludePointIndex: editingPointIndex >= 0 ? editingPointIndex : undefined,
+    const elementPoints = getLinearElementPoints(editingElement, {
+      excludePointsIndices: options.selectedPointsIndices,
     });
-    const shouldSkipFirstOrLast =
-      editingElement.points.length > 2 &&
-      pointsEqual(
-        editingElement.points[0],
-        editingElement.points[editingElement.points.length - 1],
-      );
-
-    if (shouldSkipFirstOrLast) {
-      if (editingPointIndex === 0) {
-        elementPoints.pop();
-      }
-      if (editingPointIndex === editingElement.points.length - 1) {
-        elementPoints.shift();
-      }
-    }
     allSnapPoints.push(...elementPoints);
   }
 
@@ -771,12 +753,13 @@ export const snapLinearElementPoint = (
   elements: readonly NonDeletedExcalidrawElement[],
   editingElement: ExcalidrawLinearElement,
   editingPointIndex: number,
-  pointPosition: Vector2D,
+  pointerPosition: GlobalPoint,
   app: AppClassProperties,
   event: KeyboardModifiersObject,
   elementsMap: ElementsMap,
   options: {
     includeSelfPoints?: boolean;
+    selectedPointsIndices?: readonly number[];
   } = {},
 ) => {
   if (
@@ -808,16 +791,10 @@ export const snapLinearElementPoint = (
     options,
   );
 
-  // Create a snap point for the current point position
-  const currentPointGlobal = pointFrom<GlobalPoint>(
-    pointPosition.x,
-    pointPosition.y,
-  );
-
   // Find nearest snaps
   for (const referencePoint of referenceSnapPoints) {
-    const offsetX = referencePoint[0] - currentPointGlobal[0];
-    const offsetY = referencePoint[1] - currentPointGlobal[1];
+    const offsetX = referencePoint[0] - pointerPosition[0];
+    const offsetY = referencePoint[1] - pointerPosition[1];
 
     if (Math.abs(offsetX) <= minOffset.x) {
       if (Math.abs(offsetX) < minOffset.x) {
@@ -826,7 +803,7 @@ export const snapLinearElementPoint = (
 
       nearestSnapsX.push({
         type: "point",
-        points: [currentPointGlobal, referencePoint],
+        points: [pointerPosition, referencePoint],
         offset: offsetX,
       });
 
@@ -840,7 +817,7 @@ export const snapLinearElementPoint = (
 
       nearestSnapsY.push({
         type: "point",
-        points: [currentPointGlobal, referencePoint],
+        points: [pointerPosition, referencePoint],
         offset: offsetY,
       });
 
@@ -859,8 +836,8 @@ export const snapLinearElementPoint = (
   if (snapOffset.x !== 0 || snapOffset.y !== 0) {
     // Recalculate snap lines with the snapped position
     const snappedPosition = pointFrom<GlobalPoint>(
-      pointPosition.x + snapOffset.x,
-      pointPosition.y + snapOffset.y,
+      pointerPosition[0] + snapOffset.x,
+      pointerPosition[1] + snapOffset.y,
     );
 
     const snappedSnapsX: Snaps = [];

+ 2 - 2
packages/excalidraw/components/App.tsx

@@ -5992,7 +5992,7 @@ class App extends React.Component<AppProps, AppState> {
             this.scene.getNonDeletedElements(),
             multiElement,
             points.length - 1,
-            { x: effectiveGridX, y: effectiveGridY },
+            pointFrom(effectiveGridX, effectiveGridY),
             this,
             event,
             this.scene.getNonDeletedElementsMap(),
@@ -8795,7 +8795,7 @@ class App extends React.Component<AppProps, AppState> {
               this.scene.getNonDeletedElements(),
               newElement,
               points.length - 1,
-              { x: effectiveGridX, y: effectiveGridY },
+              pointFrom(effectiveGridX, effectiveGridY),
               this,
               event,
               this.scene.getNonDeletedElementsMap(),