Browse Source

close curve for circle arrowheads

Ryan Di 7 months ago
parent
commit
bec662eaf6
2 changed files with 43 additions and 33 deletions
  1. 10 8
      packages/excalidraw/shapes.tsx
  2. 33 25
      packages/utils/geometry/shape.ts

+ 10 - 8
packages/excalidraw/shapes.tsx

@@ -1,3 +1,4 @@
+import { pointsOnBezierCurves } from "points-on-curve";
 import {
   isPoint,
   pointFrom,
@@ -8,7 +9,6 @@ import {
   type GlobalPoint,
   type LocalPoint,
   polygonFromPoints,
-  pointAdd,
 } from "../math";
 import {
   getClosedCurveShape,
@@ -16,6 +16,7 @@ import {
   getCurveShape,
   getEllipseShape,
   getFreedrawShape,
+  getPointsOnRoughCurve,
   getPolygonShape,
   type GeometricShape,
 } from "../utils/geometry/shape";
@@ -209,14 +210,15 @@ export const getElementShapes = <Point extends GlobalPoint | LocalPoint>(
         }
 
         if (arrowhead.shape === "circle") {
-          // TODO: close curve into polygon / ellipse
+          const polygonPoints = pointsOnBezierCurves(
+            getPointsOnRoughCurve(arrowhead),
+            15,
+            2,
+          ).map((p) => transform(p as Point)) as Point[];
+
           arrowheadShapes.push({
-            ...getCurveShape<Point>(
-              arrowhead,
-              element.angle,
-              center,
-              startingPoint,
-            ),
+            type: "polygon",
+            data: polygonFromPoints(polygonPoints),
             isClosed: true,
           });
         }

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

@@ -288,6 +288,35 @@ export const getFreedrawShape = <Point extends GlobalPoint | LocalPoint>(
   ) as GeometricShape<Point>;
 };
 
+export const getPointsOnRoughCurve = <Point extends GlobalPoint | LocalPoint>(
+  roughCurve: Drawable,
+) => {
+  const ops = getCurvePathOps(roughCurve);
+
+  const points: Point[] = [];
+  let odd = false;
+  for (const operation of ops) {
+    if (operation.op === "move") {
+      odd = !odd;
+      if (odd) {
+        points.push(pointFrom(operation.data[0], operation.data[1]));
+      }
+    } else if (operation.op === "bcurveTo") {
+      if (odd) {
+        points.push(pointFrom(operation.data[0], operation.data[1]));
+        points.push(pointFrom(operation.data[2], operation.data[3]));
+        points.push(pointFrom(operation.data[4], operation.data[5]));
+      }
+    } else if (operation.op === "lineTo") {
+      if (odd) {
+        points.push(pointFrom(operation.data[0], operation.data[1]));
+      }
+    }
+  }
+
+  return points;
+};
+
 export const getClosedCurveShape = <Point extends GlobalPoint | LocalPoint>(
   element: ExcalidrawLinearElement,
   roughShape: Drawable,
@@ -311,31 +340,10 @@ export const getClosedCurveShape = <Point extends GlobalPoint | LocalPoint>(
     };
   }
 
-  const ops = getCurvePathOps(roughShape);
-
-  const points: Point[] = [];
-  let odd = false;
-  for (const operation of ops) {
-    if (operation.op === "move") {
-      odd = !odd;
-      if (odd) {
-        points.push(pointFrom(operation.data[0], operation.data[1]));
-      }
-    } else if (operation.op === "bcurveTo") {
-      if (odd) {
-        points.push(pointFrom(operation.data[0], operation.data[1]));
-        points.push(pointFrom(operation.data[2], operation.data[3]));
-        points.push(pointFrom(operation.data[4], operation.data[5]));
-      }
-    } else if (operation.op === "lineTo") {
-      if (odd) {
-        points.push(pointFrom(operation.data[0], operation.data[1]));
-      }
-    }
-  }
-
-  const polygonPoints = pointsOnBezierCurves(points, 10, 5).map((p) =>
-    transform(p as Point),
+  const polygonPoints = pointsOnBezierCurves(
+    getPointsOnRoughCurve(roughShape),
+    10,
+    5,
   ) as Point[];
 
   return {