import type { MapAreaInfo, MapPen } from '@api/map'; import { MapAreaType, MapPointType } from '@api/map'; /** * 区域操作服务类 * 负责处理区域大小变化时的点位绑定检查和管理 */ export class AreaOperationService { /** * 处理区域大小变化事件 * 当互斥区或非互斥区的大小发生变化时,自动检查并绑定新包含的点位 * @param pen 发生变化的图形对象 * @param findPoints 查找点位的函数 * @param updateArea 更新区域的函数 */ public handleAreaSizeChange( pen: MapPen, findPoints: (type: string) => MapPen[], updateArea: (id: string, info: Partial) => void, ): void { // 检查是否为区域类型 if (!pen.area?.type || !['area'].includes(pen.tags?.[0] || '')) { return; } const areaType = pen.area.type; // 只处理互斥区和非互斥区 if (![MapAreaType.互斥区, MapAreaType.非互斥区].includes(areaType)) { return; } // 获取区域边界 const areaRect = this.getPenRect(pen); if (!areaRect) { return; } // 查找所有点位 const allPoints = findPoints('point') as MapPen[]; // 根据区域类型过滤点位,借鉴 addArea 的逻辑 const containedPoints: string[] = []; const currentBoundPoints = pen.area.points || []; allPoints.forEach((point) => { if (!point.point?.type) return; // 根据区域类型过滤点位类型 let shouldInclude = false; switch (areaType) { case MapAreaType.互斥区: case MapAreaType.非互斥区: // 互斥区和非互斥区绑定所有类型的点位 shouldInclude = true; break; case MapAreaType.库区: // 库区只绑定动作点 shouldInclude = point.point.type === MapPointType.动作点; break; case MapAreaType.约束区: // 约束区绑定所有类型的点位 shouldInclude = true; break; default: shouldInclude = false; break; } if (!shouldInclude) return; // 检查点位是否在区域内(使用点位中心坐标) const pointCenter = this.getPointRect(point); if (!pointCenter) return; const isContained = this.isPointInArea(pointCenter, areaRect); if (isContained) { containedPoints.push(point.id!); } }); // 找出新包含的点位(之前未绑定的) const newPoints = containedPoints.filter((id) => !currentBoundPoints.includes(id)); // 找出需要解绑的点位(之前绑定但现在不在区域内的) const removedPoints = currentBoundPoints.filter((id) => !containedPoints.includes(id)); if (newPoints.length > 0 || removedPoints.length > 0) { // 更新区域绑定的点位:保留仍在区域内的点位,添加新包含的点位 const updatedPoints = containedPoints; updateArea(pen.id!, { points: updatedPoints }); } } /** * 检查点位是否在指定区域内 * @param point 点位坐标 * @param area 区域边界 * @returns 是否在区域内 */ private isPointInArea( point: { x: number; y: number }, area: { x: number; y: number; width: number; height: number }, ): boolean { return point.x >= area.x && point.x <= area.x + area.width && point.y >= area.y && point.y <= area.y + area.height; } /** * 获取区域边界矩形 * @param pen 区域图形对象 * @returns 区域边界矩形 */ private getPenRect(pen: MapPen): { x: number; y: number; width: number; height: number } | null { if (!pen.x || !pen.y || !pen.width || !pen.height) { return null; } return { x: pen.x, y: pen.y, width: pen.width, height: pen.height, }; } /** * 获取点位中心坐标矩形 * @param pen 点位图形对象 * @returns 点位中心坐标矩形 */ private getPointRect(pen: MapPen): { x: number; y: number; width: number; height: number } | null { if (!pen.x || !pen.y || !pen.width || !pen.height) { return null; } return { x: pen.x + pen.width / 2, y: pen.y + pen.height / 2, width: pen.width, height: pen.height, }; } /** * 手动检查并更新指定区域的点位绑定 * 当用户调整区域大小后,可以调用此方法手动触发检查 * @param areaId 区域ID * @param getPenById 根据ID获取图形对象的函数 * @param findPoints 查找点位的函数 * @param updateArea 更新区域的函数 */ public checkAndUpdateAreaPoints( areaId: string, getPenById: (id: string) => MapPen | undefined, findPoints: (type: string) => MapPen[], updateArea: (id: string, info: Partial) => void, ): void { const pen = getPenById(areaId); if (!pen) { return; } this.handleAreaSizeChange(pen, findPoints, updateArea); } /** * 检查并更新所有互斥区和非互斥区的点位绑定 * 用于批量检查和更新 * @param findAreas 查找区域的函数 * @param findPoints 查找点位的函数 * @param updateArea 更新区域的函数 */ public checkAndUpdateAllAreas( findAreas: (type: string) => MapPen[], findPoints: (type: string) => MapPen[], updateArea: (id: string, info: Partial) => void, ): void { const allAreas = findAreas('area') as MapPen[]; allAreas.forEach((area) => { if (area.area?.type && [MapAreaType.互斥区, MapAreaType.非互斥区].includes(area.area.type)) { this.handleAreaSizeChange(area, findPoints, updateArea); } }); } }