refactor: 移除 EditorRobotContext 接口中的 ensureCorrectLayerOrder 方法,并在 EditorRobotService 中删除相关调用,简化代码结构

This commit is contained in:
xudan 2025-10-15 15:19:14 +08:00
parent f41e6cf1d3
commit 56f365bd04
5 changed files with 386 additions and 320 deletions

View File

@ -21,7 +21,7 @@ import type {
StandardSceneRoute,
} from '@api/scene';
import sTheme from '@core/theme.service';
import { CanvasLayer, LockState, Meta2d, type Meta2dStore, type Pen, s8 } from '@meta2d/core';
import { LockState, Meta2d, type Meta2dStore, type Pen } from '@meta2d/core';
import { useObservable } from '@vueuse/rxjs';
import { clone, get, isEmpty, isNil, isString, pick, } from 'lodash-es';
import { debounceTime, filter, map, Subject, switchMap } from 'rxjs';
@ -36,7 +36,10 @@ import {
drawStorageLocation,
drawStorageMore,
} from './draw/storage-location-drawer';
import { drawRobot,EditorRobotService } from './editor-robot.service';
import { AreaManager } from './editor/area-manager.service';
import { drawRobot,EditorRobotService } from './editor/editor-robot.service';
import { PointManager } from './editor/point-manager.service';
import { RouteManager } from './editor/route-manager.service';
import { LayerManagerService } from './layer-manager.service';
import { createStorageLocationUpdater,StorageLocationService } from './storage-location.service';
import { AutoStorageGenerator } from './utils/auto-storage-generator';
@ -65,6 +68,13 @@ export class EditorService extends Meta2d {
/** 自动生成库位工具实例 */
private readonly autoStorageGenerator!: AutoStorageGenerator;
/** 点位管理模块 */
private readonly pointManager: PointManager;
/** 路线管理模块 */
private readonly routeManager: RouteManager;
/** 区域管理模块 */
private readonly areaManager: AreaManager;
//#region 场景文件管理
/**
*
@ -1048,9 +1058,7 @@ export class EditorService extends Meta2d {
);
public getPointRect(pen?: MapPen): Rect | null {
if (isNil(pen)) return null;
const { x, y, width, height } = this.getPenRect(pen);
return { x: x + width / 2, y: y + height / 2, width, height };
return this.pointManager.getPointRect(pen);
}
/**
@ -1060,99 +1068,18 @@ export class EditorService extends Meta2d {
* @param id ID
*/
public async addPoint(p: Point, type = MapPointType., id?: string): Promise<void> {
id ||= s8();
const pointInfo: MapPointInfo = { type };
// 为充电点/停靠点设置默认启用状态
if ([MapPointType., MapPointType.].includes(type)) {
pointInfo.enabled = 1;
}
const pen: MapPen = {
...p,
...this.#mapPoint(type),
...this.#mapPointImage(type),
id,
name: 'point',
tags: ['point'],
label: `P${id}`,
point: pointInfo,
locked: LockState.DisableEdit,
};
pen.x! -= pen.width! / 2;
pen.y! -= pen.height! / 2;
const addedPen = await this.addPen(pen, false, true, true);
// 将新创建的点位移到最上层
this.layerManager.adjustElementLayer(addedPen);
// 如果是动作点且有库位信息创建库位pen对象
if (type === MapPointType. && pointInfo.associatedStorageLocations?.length) {
this.createStorageLocationPens(addedPen.id!, pointInfo.associatedStorageLocations);
}
await this.pointManager.addPoint(p, type, id);
}
public updatePoint(id: string, info: Partial<MapPointInfo>, autoCreateStorage = true): void {
const { point } = this.getPenById(id) ?? {};
if (!point?.type) return;
const o = { ...point, ...info };
this.setValue({ id, point: o }, { render: true, history: true, doEvent: true });
// 如果是动作点且库位信息发生变化重新创建库位pen对象
if (point.type === MapPointType. && info.associatedStorageLocations && autoCreateStorage) {
this.createStorageLocationPens(id, info.associatedStorageLocations);
}
this.pointManager.updatePoint(id, info, autoCreateStorage);
}
public changePointType(id: string, type: MapPointType): void {
const pen = this.getPenById(id);
const rect = this.getPointRect(pen);
if (isNil(rect)) return;
// 如果原点位是动作点,需要先删除相关的库位元素
if (pen?.point?.type === MapPointType.) {
this.removeStorageLocationPens(id);
}
const point = this.#mapPoint(type);
const pointInfo: MapPointInfo = { type };
// 为充电点/停靠点设置默认启用状态
if ([MapPointType., MapPointType.].includes(type)) {
pointInfo.enabled = 1;
}
this.setValue(
{
id,
x: rect.x - point.width / 2,
y: rect.y - point.height / 2,
...point,
...this.#mapPointImage(type),
point: pointInfo,
},
{ render: true, history: true, doEvent: true },
);
// 如果是大点位类型(需要图片),异步重新加载主题确保图片正确显示
if (type >= 10) {
requestAnimationFrame(() => {
this.reloadTheme();
});
}
this.pointManager.changePointType(id, type);
}
#mapPoint(type: MapPointType): Required<Pick<MapPen, 'width' | 'height' | 'lineWidth' | 'iconSize'>> {
const width = type < 10 ? 24 : 48;
const height = type < 10 ? 24 : 60;
const lineWidth = type < 10 ? 2 : 3;
const iconSize = type < 10 ? 4 : 10;
return { width, height, lineWidth, iconSize };
}
#mapPointImage(type: MapPointType): Required<Pick<MapPen, 'image' | 'canvasLayer'>> {
const theme = this.data().theme;
const image = type < 10 ? '' : `${import.meta.env.BASE_URL}/point/${type}-${theme}.png`;
return { image, canvasLayer: CanvasLayer.CanvasMain };
}
//#endregion
@ -1167,181 +1094,47 @@ export class EditorService extends Meta2d {
{ initialValue: new Array<MapPen>() },
);
public getRouteLabel(id?: string, d?: number): string {
if (!id) return '';
const pen = this.getPenById(id);
if (isNil(pen)) return '';
const [a1, a2] = pen.anchors ?? [];
if (!a1?.connectTo || !a2?.connectTo) return '';
const p1 = this.getPenById(a1.connectTo);
const p2 = this.getPenById(a2.connectTo);
if (isNil(p1) || isNil(p2)) return '';
const { direction = 1 } = pen.route ?? {};
return `${p1.label}${(d ?? direction) > 0 ? '→' : '←'}${p2.label}`;
public getRouteLabel(id?: string, directionOverride?: number): string {
return this.routeManager.getRouteLabel(id, directionOverride);
}
/**
* 线
* @param p
* @param type 线线
* @param id 线ID
*/
public addRoute(p: [MapPen, MapPen], type = MapRouteType.线, id?: string): void {
const [p1, p2] = p;
if (!p1?.anchors?.length || !p2?.anchors?.length) return;
const line = this.connectLine(p1, p2, undefined, undefined, false);
id ||= line.id!;
this.changePenId(line.id!, id);
const pen: MapPen = {
tags: ['route'],
route: { type },
lineWidth: 1,
locked: LockState.DisableEdit,
canvasLayer: CanvasLayer.CanvasMain,
};
this.setValue({ id, ...pen }, { render: false, history: false, doEvent: false });
this.updateLineType(line, type);
// 将路线移到底层,确保点位能覆盖在路线之上
this.bottom([line]);
this.active(id);
this.render();
this.routeManager.addRoute(p, type, id);
}
public updateRoute(id: string, info: Partial<MapRouteInfo>): void {
const { route } = this.getPenById(id) ?? {};
if (!route?.type) return;
const o = { ...route, ...info };
this.setValue({ id, route: o }, { render: true, history: true, doEvent: true });
this.routeManager.updateRoute(id, info);
}
public changeRouteType(id: string, type: MapRouteType): void {
const pen = this.getPenById(id);
if (isNil(pen)) return;
this.updateLineType(pen, type);
this.setValue({ id, route: { type } }, { render: true, history: true, doEvent: true });
this.routeManager.changeRouteType(id, type);
}
/**
* 线
* @param point1Id ID
* @param point2Id ID
* @returns 线
*/
public getRoutesBetweenPoints(point1Id: string, point2Id: string): MapPen[] {
return this.find('route').filter(route => {
const [a1, a2] = route.anchors ?? [];
if (!a1?.connectTo || !a2?.connectTo) return false;
return (a1.connectTo === point1Id && a2.connectTo === point2Id) ||
(a1.connectTo === point2Id && a2.connectTo === point1Id);
});
return this.routeManager.getRoutesBetweenPoints(point1Id, point2Id);
}
/**
* 线线
* @param routeId 线ID
* @returns 线null
*/
public getReverseRoute(routeId: string): MapPen | null {
const route = this.getPenById(routeId);
if (!route) return null;
const [a1, a2] = route.anchors ?? [];
if (!a1?.connectTo || !a2?.connectTo) return null;
const reverseRoutes = this.getRoutesBetweenPoints(a1.connectTo, a2.connectTo);
return reverseRoutes.find(r => r.id !== routeId) || null;
return this.routeManager.getReverseRoute(routeId);
}
/**
* 线
* @param p
* @param type 线线
* @param forwardId 线ID
* @param reverseId 线ID
*/
public addBidirectionalRoute(
p: [MapPen, MapPen],
type = MapRouteType.线,
forwardId?: string,
reverseId?: string
p: [MapPen, MapPen],
type = MapRouteType.线,
forwardId?: string,
reverseId?: string,
): void {
const [p1, p2] = p;
if (!p1?.anchors?.length || !p2?.anchors?.length) return;
// 创建正向路线
const forwardLine = this.connectLine(p1, p2, undefined, undefined, false);
forwardId ||= forwardLine.id!;
this.changePenId(forwardLine.id!, forwardId);
const forwardPen: MapPen = {
tags: ['route'],
route: { type, direction: 1 },
lineWidth: 1,
locked: LockState.DisableEdit,
canvasLayer: CanvasLayer.CanvasMain,
};
this.setValue({ id: forwardId, ...forwardPen }, { render: false, history: false, doEvent: false });
this.updateLineType(forwardLine, type);
// 创建反向路线
const reverseLine = this.connectLine(p2, p1, undefined, undefined, false);
reverseId ||= reverseLine.id!;
this.changePenId(reverseLine.id!, reverseId);
const reversePen: MapPen = {
tags: ['route'],
route: { type, direction: -1 },
lineWidth: 1,
locked: LockState.DisableEdit,
canvasLayer: CanvasLayer.CanvasMain,
};
this.setValue({ id: reverseId, ...reversePen }, { render: false, history: false, doEvent: false });
this.updateLineType(reverseLine, type);
// 将路线移到底层,确保点位能覆盖在路线之上
this.bottom([forwardLine, reverseLine]);
// 将反向路线移到正向路线之上,解决重叠选择问题
this.top([reverseLine]);
this.active(forwardId);
this.render();
this.routeManager.addBidirectionalRoute(p, type, forwardId, reverseId);
}
/**
* 线
* @param routeId 线ID
*/
public removeBidirectionalRoute(routeId: string): void {
const route = this.getPenById(routeId);
if (!route) return;
const [a1, a2] = route.anchors ?? [];
if (!a1?.connectTo || !a2?.connectTo) return;
// 找到所有连接这两个点位的路线
const routesBetweenPoints = this.getRoutesBetweenPoints(a1.connectTo, a2.connectTo);
// 删除所有相关路线
routesBetweenPoints.forEach(r => {
this.delete([r]);
});
this.render();
this.routeManager.removeBidirectionalRoute(routeId);
}
/**
* 线
* @param point1Id ID
* @param point2Id ID
* @returns 线
*/
public hasBidirectionalRoute(point1Id: string, point2Id: string): boolean {
const routes = this.getRoutesBetweenPoints(point1Id, point2Id);
return routes.length >= 2;
return this.routeManager.hasBidirectionalRoute(point1Id, point2Id);
}
//#endregion
//#region 区域
/** 画布上所有区域对象列表,响应式更新 */
public readonly areas = useObservable<MapPen[], MapPen[]>(
@ -1354,84 +1147,17 @@ export class EditorService extends Meta2d {
);
public getBoundAreas(id: string = '', name: 'point' | 'line', type: MapAreaType): MapPen[] {
if (!id) return [];
return this.find(`area-${type}`).filter(({ area }) => {
if (name === 'point') return area?.points?.includes(id);
if (name === 'line') return area?.routes?.includes(id);
return false;
});
return this.areaManager.getBoundAreas(id, name, type);
}
/**
*
* @param p1
* @param p2
* @param type
* @param id ID
*/
public async addArea(p1: Point, p2: Point, type = MapAreaType., id?: string) {
const w = Math.abs(p1.x - p2.x);
const h = Math.abs(p1.y - p2.y);
if (w < 50 || h < 60) return;
const points = new Array<string>();
const routes = new Array<string>();
if (!id) {
id = s8();
const selected = <MapPen[]>this.store.active;
switch (type) {
case MapAreaType.:
selected?.filter(({ point }) => point?.type === MapPointType.).forEach(({ id }) => points.push(id!));
break;
case MapAreaType.:
selected?.filter(({ point }) => point?.type).forEach(({ id }) => points.push(id!));
// 互斥区不再绑定路段
break;
case MapAreaType.:
selected?.filter(({ point }) => point?.type).forEach(({ id }) => points.push(id!));
break;
case MapAreaType.:
selected?.filter(({ point }) => point?.type).forEach(({ id }) => points.push(id!));
break;
case MapAreaType.:
// 描述区不需要绑定点位或路线
break;
default:
break;
}
}
const areaInfo: MapAreaInfo = { type, points, routes };
if (type === MapAreaType.) {
areaInfo.inoutflag = 2;
}
const pen: MapPen = {
id,
name: 'area',
tags: ['area', `area-${type}`],
label: `A${id}`,
x: Math.min(p1.x, p2.x),
y: Math.min(p1.y, p2.y),
width: w,
height: h,
lineWidth: 1,
area: areaInfo,
locked: LockState.None,
};
const area = await this.addPen(pen, true, true, true);
this.bottom(area);
// 如果是库区且包含动作点,触发自动生成库位的确认对话框
if (type === MapAreaType. && points.length > 0) {
this.autoStorageGenerator.triggerAutoCreateStorageDialog(pen, points);
}
public async addArea(p1: Point, p2: Point, type = MapAreaType., id?: string): Promise<void> {
await this.areaManager.addArea(p1, p2, type, id);
}
public updateArea(id: string, info: Partial<MapAreaInfo>): void {
const { area } = this.getPenById(id) ?? {};
if (!area?.type) return;
const o = { ...area, ...info };
this.setValue({ id, area: o }, { render: true, history: true, doEvent: true });
this.areaManager.updateArea(id, info);
}
//#endregion
/**
@ -1458,6 +1184,10 @@ export class EditorService extends Meta2d {
// 初始化BinTask管理服务
this.binTaskManager = new BinTaskManagerService(this);
this.pointManager = new PointManager(this, this.layerManager);
this.routeManager = new RouteManager(this);
this.areaManager = new AreaManager(this, this.autoStorageGenerator);
// 设置颜色配置服务的编辑器实例
colorConfig.setEditorService(this);
@ -1499,11 +1229,7 @@ export class EditorService extends Meta2d {
this.setTheme(theme);
this.setOptions({ color: get(sTheme.editor, 'color') });
this.find('point').forEach((pen) => {
if (!pen.point?.type) return;
if (pen.point.type < 10) return;
this.canvas.updateValue(pen, this.#mapPointImage(pen.point.type));
});
this.pointManager.refreshPointImages();
this.find('robot').forEach((pen) => {
if (!pen.robot?.type) return;
// 从pen的text属性获取机器人名称

View File

@ -0,0 +1,90 @@
import {
type MapAreaInfo,
MapAreaType,
type MapPen,
MapPointType,
type Point,
} from '@api/map';
import { LockState, s8 } from '@meta2d/core';
import type { EditorService } from '../editor.service';
import type { AutoStorageGenerator } from '../utils/auto-storage-generator';
export class AreaManager {
constructor(
private readonly editor: EditorService,
private readonly autoStorageGenerator: AutoStorageGenerator,
) {}
public getBoundAreas(id: string, name: 'point' | 'line', type: MapAreaType): MapPen[] {
if (!id) return [];
return this.editor.find(`area-${type}`).filter(({ area }) => {
if (name === 'point') return area?.points?.includes(id);
if (name === 'line') return area?.routes?.includes(id);
return false;
});
}
public async addArea(p1: Point, p2: Point, type = MapAreaType., id?: string): Promise<void> {
const w = Math.abs(p1.x - p2.x);
const h = Math.abs(p1.y - p2.y);
if (w < 50 || h < 60) return;
const points: string[] = [];
const routes: string[] = [];
if (!id) {
id = s8();
const selected = this.editor.store.active as MapPen[] | undefined;
switch (type) {
case MapAreaType.:
selected
?.filter(({ point }) => point?.type === MapPointType.)
.forEach(({ id: pointId }) => points.push(pointId!));
break;
case MapAreaType.:
case MapAreaType.:
case MapAreaType.:
selected?.filter(({ point }) => point?.type).forEach(({ id: pointId }) => points.push(pointId!));
break;
case MapAreaType.:
break;
default:
break;
}
}
const areaInfo: MapAreaInfo = { type, points, routes };
if (type === MapAreaType.) {
areaInfo.inoutflag = 2;
}
const pen: MapPen = {
id,
name: 'area',
tags: ['area', `area-${type}`],
label: `A${id}`,
x: Math.min(p1.x, p2.x),
y: Math.min(p1.y, p2.y),
width: w,
height: h,
lineWidth: 1,
area: areaInfo,
locked: LockState.None,
};
const area = await this.editor.addPen(pen, true, true, true);
this.editor.bottom(area);
if (type === MapAreaType. && points.length > 0) {
this.autoStorageGenerator.triggerAutoCreateStorageDialog(pen, points);
}
}
public updateArea(id: string, info: Partial<MapAreaInfo>): void {
const { area } = this.editor.getPenById(id) ?? {};
if (!area?.type) return;
const mergedArea = { ...area, ...info };
this.editor.setValue({ id, area: mergedArea }, { render: true, history: true, doEvent: true });
}
}

View File

@ -11,7 +11,7 @@ import { reactive } from 'vue';
import cargoIcon from '../assets/icons/png/cargo.png';
import chargingIcon from '../assets/icons/png/charging.png';
import notAcceptingOrdersIcon from '../assets/icons/png/notAcceptingOrders.png';
import colorConfig from './color/color-config.service';
import colorConfig from '../color/color-config.service';
interface RobotStatusPosition {
x: number;
@ -31,7 +31,6 @@ export interface EditorRobotContext {
getPenRect(pen: MapPen): Rect;
find(target: string): MapPen[];
inactive(): void;
ensureCorrectLayerOrder(): void;
}
export class EditorRobotService {
@ -276,7 +275,6 @@ export class EditorRobotService {
}),
);
this.ctx.ensureCorrectLayerOrder();
}
public updateAllRobotImageSizes(): void {

View File

@ -0,0 +1,117 @@
import { type MapPen, type MapPointInfo, MapPointType, type Point, type Rect } from '@api/map';
import { CanvasLayer, LockState, s8 } from '@meta2d/core';
import { isNil } from 'lodash-es';
import type { EditorService } from '../editor.service';
import type { LayerManagerService } from '../layer-manager.service';
export class PointManager {
constructor(
private readonly editor: EditorService,
private readonly layerManager: LayerManagerService,
) {}
public getPointRect(pen?: MapPen): Rect | null {
if (isNil(pen)) return null;
const { x, y, width, height } = this.editor.getPenRect(pen);
return { x: x + width / 2, y: y + height / 2, width, height };
}
public async addPoint(p: Point, type = MapPointType., id?: string): Promise<void> {
id ||= s8();
const pointInfo: MapPointInfo = { type };
if ([MapPointType., MapPointType.].includes(type)) {
pointInfo.enabled = 1;
}
const pen: MapPen = {
...p,
...this.mapPoint(type),
...this.mapPointImage(type),
id,
name: 'point',
tags: ['point'],
label: `P${id}`,
point: pointInfo,
locked: LockState.DisableEdit,
};
pen.x! -= pen.width! / 2;
pen.y! -= pen.height! / 2;
const addedPen = await this.editor.addPen(pen, false, true, true);
this.layerManager.adjustElementLayer(addedPen);
if (type === MapPointType. && pointInfo.associatedStorageLocations?.length) {
this.editor.createStorageLocationPens(addedPen.id!, pointInfo.associatedStorageLocations);
}
}
public updatePoint(id: string, info: Partial<MapPointInfo>, autoCreateStorage = true): void {
const { point } = this.editor.getPenById(id) ?? {};
if (!point?.type) return;
const mergedPoint = { ...point, ...info };
this.editor.setValue({ id, point: mergedPoint }, { render: true, history: true, doEvent: true });
if (point.type === MapPointType. && info.associatedStorageLocations && autoCreateStorage) {
this.editor.createStorageLocationPens(id, info.associatedStorageLocations);
}
}
public changePointType(id: string, type: MapPointType): void {
const pen = this.editor.getPenById(id);
const rect = this.getPointRect(pen);
if (isNil(rect)) return;
if (pen?.point?.type === MapPointType.) {
this.editor.removeStorageLocationPens(id);
}
const point = this.mapPoint(type);
const pointInfo: MapPointInfo = { type };
if ([MapPointType., MapPointType.].includes(type)) {
pointInfo.enabled = 1;
}
this.editor.setValue(
{
id,
x: rect.x - point.width / 2,
y: rect.y - point.height / 2,
...point,
...this.mapPointImage(type),
point: pointInfo,
},
{ render: true, history: true, doEvent: true },
);
if (type >= 10) {
requestAnimationFrame(() => {
this.editor.reloadTheme();
});
}
}
public refreshPointImages(): void {
this.editor.find('point').forEach((pen) => {
const type = pen.point?.type;
if (!type || type < 10) return;
this.editor.canvas.updateValue(pen, this.mapPointImage(type));
});
}
private mapPoint(type: MapPointType): Required<Pick<MapPen, 'width' | 'height' | 'lineWidth' | 'iconSize'>> {
const width = type < 10 ? 24 : 48;
const height = type < 10 ? 24 : 60;
const lineWidth = type < 10 ? 2 : 3;
const iconSize = type < 10 ? 4 : 10;
return { width, height, lineWidth, iconSize };
}
private mapPointImage(type: MapPointType): Required<Pick<MapPen, 'image' | 'canvasLayer'>> {
const theme = this.editor.data().theme;
const image = type < 10 ? '' : `${import.meta.env.BASE_URL}/point/${type}-${theme}.png`;
return { image, canvasLayer: CanvasLayer.CanvasMain };
}
}

View File

@ -0,0 +1,135 @@
import { type MapPen, type MapRouteInfo, MapRouteType } from '@api/map';
import { CanvasLayer, LockState } from '@meta2d/core';
import type { EditorService } from '../editor.service';
export class RouteManager {
constructor(private readonly editor: EditorService) {}
public getRouteLabel(id?: string, overrideDirection?: number): string {
if (!id) return '';
const pen = this.editor.getPenById(id);
if (!pen) return '';
const [a1, a2] = pen.anchors ?? [];
if (!a1?.connectTo || !a2?.connectTo) return '';
const p1 = this.editor.getPenById(a1.connectTo);
const p2 = this.editor.getPenById(a2.connectTo);
if (!p1 || !p2) return '';
const { direction = 1 } = pen.route ?? {};
return `${p1.label}${(overrideDirection ?? direction) > 0 ? '→' : '←'}${p2.label}`;
}
public addRoute(p: [MapPen, MapPen], type = MapRouteType.线, id?: string): void {
const [p1, p2] = p;
if (!p1?.anchors?.length || !p2?.anchors?.length) return;
const line = this.editor.connectLine(p1, p2, undefined, undefined, false);
id ||= line.id!;
this.editor.changePenId(line.id!, id);
const pen: MapPen = {
tags: ['route'],
route: { type },
lineWidth: 1,
locked: LockState.DisableEdit,
canvasLayer: CanvasLayer.CanvasMain,
};
this.editor.setValue({ id, ...pen }, { render: false, history: false, doEvent: false });
this.editor.updateLineType(line, type);
this.editor.bottom([line]);
this.editor.active(id);
this.editor.render();
}
public updateRoute(id: string, info: Partial<MapRouteInfo>): void {
const { route } = this.editor.getPenById(id) ?? {};
if (!route?.type) return;
const mergedRoute = { ...route, ...info };
this.editor.setValue({ id, route: mergedRoute }, { render: true, history: true, doEvent: true });
}
public changeRouteType(id: string, type: MapRouteType): void {
const pen = this.editor.getPenById(id);
if (!pen) return;
this.editor.updateLineType(pen, type);
this.editor.setValue({ id, route: { type } }, { render: true, history: true, doEvent: true });
}
public getRoutesBetweenPoints(point1Id: string, point2Id: string): MapPen[] {
return this.editor.find('route').filter((route) => {
const [a1, a2] = route.anchors ?? [];
if (!a1?.connectTo || !a2?.connectTo) return false;
return (
(a1.connectTo === point1Id && a2.connectTo === point2Id) ||
(a1.connectTo === point2Id && a2.connectTo === point1Id)
);
});
}
public getReverseRoute(routeId: string): MapPen | null {
const route = this.editor.getPenById(routeId);
if (!route) return null;
const [a1, a2] = route.anchors ?? [];
if (!a1?.connectTo || !a2?.connectTo) return null;
const reverseRoutes = this.getRoutesBetweenPoints(a1.connectTo, a2.connectTo);
return reverseRoutes.find((r) => r.id !== routeId) || null;
}
public addBidirectionalRoute(
p: [MapPen, MapPen],
type = MapRouteType.线,
forwardId?: string,
reverseId?: string,
): void {
const [p1, p2] = p;
if (!p1?.anchors?.length || !p2?.anchors?.length) return;
const forwardLine = this.editor.connectLine(p1, p2, undefined, undefined, false);
forwardId ||= forwardLine.id!;
this.editor.changePenId(forwardLine.id!, forwardId);
const forwardPen: MapPen = {
tags: ['route'],
route: { type, direction: 1 },
lineWidth: 1,
locked: LockState.DisableEdit,
canvasLayer: CanvasLayer.CanvasMain,
};
this.editor.setValue({ id: forwardId, ...forwardPen }, { render: false, history: false, doEvent: false });
this.editor.updateLineType(forwardLine, type);
const reverseLine = this.editor.connectLine(p2, p1, undefined, undefined, false);
reverseId ||= reverseLine.id!;
this.editor.changePenId(reverseLine.id!, reverseId);
const reversePen: MapPen = {
tags: ['route'],
route: { type, direction: -1 },
lineWidth: 1,
locked: LockState.DisableEdit,
canvasLayer: CanvasLayer.CanvasMain,
};
this.editor.setValue({ id: reverseId, ...reversePen }, { render: false, history: false, doEvent: false });
this.editor.updateLineType(reverseLine, type);
this.editor.bottom([forwardLine, reverseLine]);
this.editor.top([reverseLine]);
this.editor.active(forwardId);
this.editor.render();
}
public removeBidirectionalRoute(routeId: string): void {
const route = this.editor.getPenById(routeId);
if (!route) return;
const [a1, a2] = route.anchors ?? [];
if (!a1?.connectTo || !a2?.connectTo) return;
const routesBetweenPoints = this.getRoutesBetweenPoints(a1.connectTo, a2.connectTo);
routesBetweenPoints.forEach((r) => {
this.editor.delete([r]);
});
this.editor.render();
}
public hasBidirectionalRoute(point1Id: string, point2Id: string): boolean {
const routes = this.getRoutesBetweenPoints(point1Id, point2Id);
return routes.length >= 2;
}
}