web-map/src/services/area-operation.service.ts

187 lines
5.6 KiB
TypeScript

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<MapAreaInfo>) => 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<MapAreaInfo>) => 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<MapAreaInfo>) => 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);
}
});
}
}