feat(area-manager): 优化门区域路段绑定逻辑,支持线段与矩形相交判定

This commit is contained in:
xudan 2025-10-21 14:49:00 +08:00
parent f6e8a225cd
commit 151534ae38

View File

@ -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.) {