diff --git a/src/services/editor/area-manager.service.ts b/src/services/editor/area-manager.service.ts index f062baf..d0d4bdd 100644 --- a/src/services/editor/area-manager.service.ts +++ b/src/services/editor/area-manager.service.ts @@ -55,8 +55,10 @@ export class AreaManager { } } - // 门区域:自动绑定矩形内的路段(采用路段两端点均在区域内的简单判定) - if ((type as any) === (DOOR_AREA_TYPE as any)) { + // 门区域:自动绑定与矩形相交或端点落入矩形的路段 + // 原逻辑仅在两端点都在矩形内时绑定; + // 现在扩展为:端点都在内 OR 线段与矩形任一边相交,则绑定。 + if ((type as any) === (DOOR_AREA_TYPE as any)) { const areaRect = { x: Math.min(p1.x, p2.x), y: Math.min(p1.y, p2.y), @@ -66,7 +68,70 @@ export class AreaManager { const isInRect = (x: number, y: number) => x >= areaRect.x && x <= areaRect.x + areaRect.width && y >= areaRect.y && y <= areaRect.y + areaRect.height; - const allRoutes = this.editor.find('route'); + // 线段与矩形是否相交(含端点在内)的简易判断: + // 1) 任一点在矩形内 + // 2) 与矩形四条边任意一条有线段相交 + const segmentIntersectsRect = ( + x1: number, + y1: number, + x2: number, + y2: number, + rx: number, + ry: number, + rw: number, + rh: number, + ): boolean => { + const xMin = rx; + const xMax = rx + rw; + const yMin = ry; + const yMax = ry + rh; + + const pointInRect = (x: number, y: number) => x >= xMin && x <= xMax && y >= yMin && y <= yMax; + if (pointInRect(x1, y1) || pointInRect(x2, y2)) return true; + + // 线段相交判定(跨立实验) + const segmentsIntersect = ( + ax1: number, + ay1: number, + ax2: number, + ay2: number, + bx1: number, + by1: number, + bx2: number, + by2: number, + ): boolean => { + const d = (x: number, y: number, x1: number, y1: number, x2: number, y2: number) => + (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1); + const onSegment = (x: number, y: number, x1: number, y1: number, x2: number, y2: number) => + Math.min(x1, x2) <= x && x <= Math.max(x1, x2) && Math.min(y1, y2) <= y && y <= Math.max(y1, y2); + const d1 = d(ax1, ay1, bx1, by1, bx2, by2); + const d2 = d(ax2, ay2, bx1, by1, bx2, by2); + const d3 = d(bx1, by1, ax1, ay1, ax2, ay2); + const d4 = d(bx2, by2, ax1, ay1, ax2, ay2); + if (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) return true; + // 共线且端点在线段投影上 + if (d1 === 0 && onSegment(ax1, ay1, bx1, by1, bx2, by2)) return true; + if (d2 === 0 && onSegment(ax2, ay2, bx1, by1, bx2, by2)) return true; + if (d3 === 0 && onSegment(bx1, by1, ax1, ay1, ax2, ay2)) return true; + if (d4 === 0 && onSegment(bx2, by2, ax1, ay1, ax2, ay2)) return true; + return false; + }; + + // 矩形四条边 + const leftX = xMin; + const rightX = xMax; + const topY = yMin; + const bottomY = yMax; + + if (segmentsIntersect(x1, y1, x2, y2, leftX, topY, leftX, bottomY)) return true; // 左边 + if (segmentsIntersect(x1, y1, x2, y2, rightX, topY, rightX, bottomY)) return true; // 右边 + if (segmentsIntersect(x1, y1, x2, y2, leftX, topY, rightX, topY)) return true; // 上边 + if (segmentsIntersect(x1, y1, x2, y2, leftX, bottomY, rightX, bottomY)) return true; // 下边 + + return false; + }; + + const allRoutes = this.editor.find('route'); allRoutes.forEach((r) => { const [a1, a2] = r.anchors ?? []; if (!a1?.connectTo || !a2?.connectTo) return; @@ -80,11 +145,22 @@ export class AreaManager { const cay = (ra.y ?? 0) + (ra.height ?? 0) / 2; const cbx = (rb.x ?? 0) + (rb.width ?? 0) / 2; const cby = (rb.y ?? 0) + (rb.height ?? 0) / 2; - if (isInRect(cax, cay) && isInRect(cbx, cby)) { - routes.push(r.id!); - } - }); - } + const bothInside = isInRect(cax, cay) && isInRect(cbx, cby); + const intersects = segmentIntersectsRect( + cax, + cay, + cbx, + cby, + areaRect.x, + areaRect.y, + areaRect.width, + areaRect.height, + ); + if (bothInside || intersects) { + routes.push(r.id!); + } + }); + } const areaInfo: MapAreaInfo = { type, points, routes }; if (type === MapAreaType.库区) {