Pārlūkot izejas kodu

fix: resize non solid lines/arrows/draws (#1608)

Daishi Kato 5 gadi atpakaļ
vecāks
revīzija
6b628bb1a6
2 mainītis faili ar 78 papildinājumiem un 92 dzēšanām
  1. 9 8
      src/element/bounds.ts
  2. 69 84
      src/renderer/renderElement.ts

+ 9 - 8
src/element/bounds.ts

@@ -1,9 +1,12 @@
 import { ExcalidrawElement, ExcalidrawLinearElement } from "./types";
 import { rotate } from "../math";
 import rough from "roughjs/bin/rough";
-import { Drawable, Op, Options } from "roughjs/bin/core";
+import { Drawable, Op } from "roughjs/bin/core";
 import { Point } from "../types";
-import { getShapeForElement } from "../renderer/renderElement";
+import {
+  getShapeForElement,
+  generateRoughOptions,
+} from "../renderer/renderElement";
 import { isLinearElement } from "./typeChecks";
 import { rescalePoints } from "../points";
 
@@ -323,13 +326,11 @@ export const getResizedElementAbsoluteCoords = (
     rescalePoints(1, nextHeight, element.points),
   );
 
-  const options: Options = {
-    strokeWidth: element.strokeWidth,
-    roughness: element.roughness,
-    seed: element.seed,
-  };
   const gen = rough.generator();
-  const curve = gen.curve(points as [number, number][], options);
+  const curve = gen.curve(
+    points as [number, number][],
+    generateRoughOptions(element),
+  );
   const ops = getCurvePathOps(curve);
   const [minX, minY, maxX, maxY] = getMinMaxXYFromCurvePathOps(ops);
   return [

+ 69 - 84
src/renderer/renderElement.ts

@@ -152,53 +152,88 @@ export function invalidateShapeForElement(element: ExcalidrawElement) {
   shapeCache.delete(element);
 }
 
-function generateElement(
-  element: NonDeletedExcalidrawElement,
-  generator: RoughGenerator,
-  sceneState?: SceneState,
-) {
-  let shape = shapeCache.get(element) || null;
-  if (!shape) {
-    elementWithCanvasCache.delete(element);
-
-    const strokeLineDash =
+export function generateRoughOptions(element: ExcalidrawElement): Options {
+  const options: Options = {
+    seed: element.seed,
+    strokeLineDash:
       element.strokeStyle === "dashed"
         ? DASHARRAY_DASHED
         : element.strokeStyle === "dotted"
         ? DASHARRAY_DOTTED
-        : undefined;
+        : undefined,
     // for non-solid strokes, disable multiStroke because it tends to make
     //  dashes/dots overlay each other
-    const disableMultiStroke = element.strokeStyle !== "solid";
+    disableMultiStroke: element.strokeStyle !== "solid",
     // for non-solid strokes, increase the width a bit to make it visually
     //  similar to solid strokes, because we're also disabling multiStroke
-    const strokeWidth =
+    strokeWidth:
       element.strokeStyle !== "solid"
         ? element.strokeWidth + 0.5
-        : element.strokeWidth;
+        : element.strokeWidth,
     // when increasing strokeWidth, we must explicitly set fillWeight and
     //  hachureGap because if not specified, roughjs uses strokeWidth to
     //  calculate them (and we don't want the fills to be modified)
-    const fillWeight = element.strokeWidth / 2;
-    const hachureGap = element.strokeWidth * 4;
+    fillWeight: element.strokeWidth / 2,
+    hachureGap: element.strokeWidth * 4,
+    roughness: element.roughness,
+    stroke: element.strokeColor,
+  };
 
-    switch (element.type) {
-      case "rectangle":
-        shape = generator.rectangle(0, 0, element.width, element.height, {
-          strokeWidth,
-          fillWeight,
-          hachureGap,
-          strokeLineDash,
-          disableMultiStroke,
-          stroke: element.strokeColor,
-          fill:
+  switch (element.type) {
+    case "rectangle":
+    case "diamond":
+    case "ellipse": {
+      options.fillStyle = element.fillStyle;
+      options.fill =
+        element.backgroundColor === "transparent"
+          ? undefined
+          : element.backgroundColor;
+      if (element.type === "ellipse") {
+        options.curveFitting = 1;
+      }
+      return options;
+    }
+    case "line":
+    case "draw":
+    case "arrow": {
+      // If shape is a line and is a closed shape,
+      // fill the shape if a color is set.
+      if (element.type === "line" || element.type === "draw") {
+        if (isPathALoop(element.points)) {
+          options.fillStyle = element.fillStyle;
+          options.fill =
             element.backgroundColor === "transparent"
               ? undefined
-              : element.backgroundColor,
-          fillStyle: element.fillStyle,
-          roughness: element.roughness,
-          seed: element.seed,
-        });
+              : element.backgroundColor;
+        }
+      }
+
+      return options;
+    }
+    default: {
+      throw new Error(`Unimplemented type ${element.type}`);
+    }
+  }
+}
+
+function generateElement(
+  element: NonDeletedExcalidrawElement,
+  generator: RoughGenerator,
+  sceneState?: SceneState,
+) {
+  let shape = shapeCache.get(element) || null;
+  if (!shape) {
+    elementWithCanvasCache.delete(element);
+
+    switch (element.type) {
+      case "rectangle":
+        shape = generator.rectangle(
+          0,
+          0,
+          element.width,
+          element.height,
+          generateRoughOptions(element),
+        );
 
         break;
       case "diamond": {
@@ -219,21 +254,7 @@ function generateElement(
             [bottomX, bottomY],
             [leftX, leftY],
           ],
-          {
-            strokeWidth,
-            fillWeight,
-            hachureGap,
-            strokeLineDash,
-            disableMultiStroke,
-            stroke: element.strokeColor,
-            fill:
-              element.backgroundColor === "transparent"
-                ? undefined
-                : element.backgroundColor,
-            fillStyle: element.fillStyle,
-            roughness: element.roughness,
-            seed: element.seed,
-          },
+          generateRoughOptions(element),
         );
         break;
       }
@@ -243,54 +264,18 @@ function generateElement(
           element.height / 2,
           element.width,
           element.height,
-          {
-            strokeWidth,
-            fillWeight,
-            hachureGap,
-            strokeLineDash,
-            disableMultiStroke,
-            stroke: element.strokeColor,
-            fill:
-              element.backgroundColor === "transparent"
-                ? undefined
-                : element.backgroundColor,
-            fillStyle: element.fillStyle,
-            roughness: element.roughness,
-            seed: element.seed,
-            curveFitting: 1,
-          },
+          generateRoughOptions(element),
         );
         break;
       case "line":
       case "draw":
       case "arrow": {
-        const options: Options = {
-          strokeWidth,
-          fillWeight,
-          hachureGap,
-          strokeLineDash,
-          disableMultiStroke,
-          stroke: element.strokeColor,
-          seed: element.seed,
-          roughness: element.roughness,
-        };
+        const options = generateRoughOptions(element);
 
         // points array can be empty in the beginning, so it is important to add
         // initial position to it
         const points = element.points.length ? element.points : [[0, 0]];
 
-        // If shape is a line and is a closed shape,
-        // fill the shape if a color is set.
-        if (element.type === "line" || element.type === "draw") {
-          if (isPathALoop(element.points)) {
-            options.fillStyle = element.fillStyle;
-            options.fill =
-              element.backgroundColor === "transparent"
-                ? undefined
-                : element.backgroundColor;
-          }
-        }
-
         // curve is always the first element
         // this simplifies finding the curve for an element
         shape = [generator.curve(points as [number, number][], options)];