Просмотр исходного кода

fix(line): hover detection of the fold line (#167)

Louis Young 8 месяцев назад
Родитель
Сommit
cb942616ab
1 измененных файлов с 62 добавлено и 31 удалено
  1. 62 31
      packages/plugins/free-lines-plugin/src/contributions/fold/fold-line.ts

+ 62 - 31
packages/plugins/free-lines-plugin/src/contributions/fold/fold-line.ts

@@ -1,26 +1,46 @@
 import { type IPoint, Point, Rectangle } from '@flowgram.ai/utils';
 
 /**
- * 计算点和直线的距离
- * @param p1
- * @param p2
- * @param p3
+ * 计算点到线段的距离
+ * @param point 待测试点
+ * @param segStart 线段起点
+ * @param segEnd 线段终点
  */
-function pointLineDistance(p1: IPoint, p2: IPoint, p3: IPoint): number {
-  let len;
+const getPointToSegmentDistance = (point: IPoint, segStart: IPoint, segEnd: IPoint): number => {
+  const { x: px, y: py } = point;
+  const { x: x1, y: y1 } = segStart;
+  const { x: x2, y: y2 } = segEnd;
 
-  // 竖着的线
-  if (p1.x - p2.x === 0) {
-    len = Math.abs(p3.x - p1.x);
-  } else {
-    const A = (p1.y - p2.y) / (p1.x - p2.x);
-    const B = p1.y - A * p1.x;
+  const A = px - x1;
+  const B = py - y1;
+  const C = x2 - x1;
+  const D = y2 - y1;
+
+  const dot = A * C + B * D;
+  const lenSq = C * C + D * D;
+
+  // 参数方程中的t参数
+  const param = lenSq === 0 ? -1 : dot / lenSq;
+
+  let xx: number;
+  let yy: number;
 
-    len = Math.abs((A * p3.x + B - p3.y) / Math.sqrt(A * A + 1));
+  if (param < 0) {
+    xx = x1;
+    yy = y1;
+  } else if (param > 1) {
+    xx = x2;
+    yy = y2;
+  } else {
+    xx = x1 + param * C;
+    yy = y1 + param * D;
   }
 
-  return len;
-}
+  const dx = px - xx;
+  const dy = py - yy;
+
+  return Math.sqrt(dx * dx + dy * dy);
+};
 
 export namespace FoldLine {
   const EDGE_RADIUS = 5;
@@ -218,22 +238,33 @@ export namespace FoldLine {
     );
   }
   /**
-   * 折叠线和点的距离
-   * @param linePosition
-   * @param pos
+   * 计算点到折线的最短距离
+   * @param points 折线的所有端点
+   * @param pos 待测试点
+   * @returns 最短距离
    */
-  export function getFoldLineToPointDistance(points: IPoint[], pos: IPoint): number {
-    const bounds = getBounds(points);
-    if (bounds.contains(pos.x, pos.y)) {
-      const lines = points.reduce((res, point, index) => {
-        if (index === 0) {
-          return res;
-        }
-        res.push([points[index - 1]!, point]);
-        return res;
-      }, [] as [IPoint, IPoint][]);
-      return Math.min(...lines.map((l) => pointLineDistance(...l, pos)));
+  export const getFoldLineToPointDistance = (points: IPoint[], pos: IPoint): number => {
+    // 特殊情况处理
+    if (points.length === 0) {
+      return Infinity;
     }
-    return Math.min(...points.map((p) => Point.getDistance(p, pos)));
-  }
+
+    if (points.length === 1) {
+      return Point.getDistance(points[0]!, pos);
+    }
+
+    // 构建线段数组
+    const lines: [IPoint, IPoint][] = [];
+    for (let i = 0; i < points.length - 1; i++) {
+      lines.push([points[i]!, points[i + 1]!]);
+    }
+
+    // 计算点到每个线段的最短距离
+    const distances = lines.map((line) => {
+      const [p1, p2] = line;
+      return getPointToSegmentDistance(pos, p1, p2);
+    });
+
+    return Math.min(...distances);
+  };
 }