Browse Source

fix: multiple elements resizing regressions (#5586)

Alex Kim 2 years ago
parent
commit
f5379d1563
3 changed files with 54 additions and 33 deletions
  1. 3 3
      src/element/bounds.test.ts
  2. 33 17
      src/element/bounds.ts
  3. 18 13
      src/element/resizeElements.ts

+ 3 - 3
src/element/bounds.test.ts

@@ -22,7 +22,7 @@ const _ce = ({
     backgroundColor: "#000",
     fillStyle: "solid",
     strokeWidth: 1,
-    roughness: 1,
+    roughness: 0,
     opacity: 1,
     x,
     y,
@@ -106,7 +106,7 @@ describe("getElementBounds", () => {
     } as ExcalidrawLinearElement);
     expect(x1).toEqual(360.3176068760539);
     expect(y1).toEqual(185.90654264413516);
-    expect(x2).toEqual(473.8171188951176);
-    expect(y2).toEqual(320.391865303557);
+    expect(x2).toEqual(480.87005902729743);
+    expect(y2).toEqual(320.4751269334226);
   });
 });

+ 33 - 17
src/element/bounds.ts

@@ -387,34 +387,49 @@ export const getArrowheadPoints = (
   return [x2, y2, x3, y3, x4, y4];
 };
 
+const generateLinearElementShape = (
+  element: ExcalidrawLinearElement,
+): Drawable => {
+  const generator = rough.generator();
+  const options = generateRoughOptions(element);
+
+  const method = (() => {
+    if (element.strokeSharpness !== "sharp") {
+      return "curve";
+    }
+    if (options.fill) {
+      return "polygon";
+    }
+    return "linearPath";
+  })();
+
+  return generator[method](element.points as Mutable<Point>[], options);
+};
+
 const getLinearElementRotatedBounds = (
   element: ExcalidrawLinearElement,
   cx: number,
   cy: number,
 ): [number, number, number, number] => {
-  if (element.points.length < 2 || !getShapeForElement(element)) {
-    // XXX this is just a poor estimate and not very useful
-    const { minX, minY, maxX, maxY } = element.points.reduce(
-      (limits, [x, y]) => {
-        [x, y] = rotate(element.x + x, element.y + y, cx, cy, element.angle);
-        limits.minY = Math.min(limits.minY, y);
-        limits.minX = Math.min(limits.minX, x);
-        limits.maxX = Math.max(limits.maxX, x);
-        limits.maxY = Math.max(limits.maxY, y);
-        return limits;
-      },
-      { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity },
+  if (element.points.length < 2) {
+    const [pointX, pointY] = element.points[0];
+    const [x, y] = rotate(
+      element.x + pointX,
+      element.y + pointY,
+      cx,
+      cy,
+      element.angle,
     );
-    return [minX, minY, maxX, maxY];
+    return [x, y, x, y];
   }
 
-  const shape = getShapeForElement(element)!;
-
   // first element is always the curve
-  const ops = getCurvePathOps(shape[0]);
-
+  const cachedShape = getShapeForElement(element)?.[0];
+  const shape = cachedShape ?? generateLinearElementShape(element);
+  const ops = getCurvePathOps(shape);
   const transformXY = (x: number, y: number) =>
     rotate(element.x + x, element.y + y, cx, cy, element.angle);
+
   return getMinMaxXYFromCurvePathOps(ops, transformXY);
 };
 
@@ -538,6 +553,7 @@ export const getResizedElementAbsoluteCoords = (
             points as [number, number][],
             generateRoughOptions(element),
           );
+
     const ops = getCurvePathOps(curve);
     bounds = getMinMaxXYFromCurvePathOps(ops);
   }

+ 18 - 13
src/element/resizeElements.ts

@@ -721,7 +721,7 @@ const resizeMultipleElements = (
       (pointerSideY * Math.abs(pointerY - anchorY)) / (maxY - minY),
     ) * (shouldResizeFromCenter ? 2 : 1);
 
-  if (scale === 1) {
+  if (scale === 0) {
     return;
   }
 
@@ -766,21 +766,26 @@ const resizeMultipleElements = (
         width - optionalPadding,
         height - optionalPadding,
       );
-      if (textMeasurements) {
-        if (isTextElement(element.orig)) {
-          update.fontSize = textMeasurements.size;
-          update.baseline = textMeasurements.baseline;
-        }
-
-        if (boundTextElement) {
-          boundTextUpdates = {
-            fontSize: textMeasurements.size,
-            baseline: textMeasurements.baseline,
-          };
-        }
+
+      if (!textMeasurements) {
+        return;
+      }
+
+      if (isTextElement(element.orig)) {
+        update.fontSize = textMeasurements.size;
+        update.baseline = textMeasurements.baseline;
+      }
+
+      if (boundTextElement) {
+        boundTextUpdates = {
+          fontSize: textMeasurements.size,
+          baseline: textMeasurements.baseline,
+        };
       }
     }
 
+    updateBoundElements(element.latest, { newSize: { width, height } });
+
     mutateElement(element.latest, update);
 
     if (boundTextElement && boundTextUpdates) {