diff --git a/src/pages/group-editor.vue b/src/pages/group-editor.vue index c6f83f6..519063b 100644 --- a/src/pages/group-editor.vue +++ b/src/pages/group-editor.vue @@ -63,7 +63,7 @@ const importScene = async () => { const file = await selectFile('.scene'); if (!file?.size) return; const json = await decodeTextFile(file); - editor.value?.load(json, editable.value, detail.value ?? {}); + editor.value?.load(json, editable.value, detail.value ?? {}, true); // 第四个参数isImport=true }; const exportScene = () => { const json = editor.value?.save(); diff --git a/src/pages/scene-editor.vue b/src/pages/scene-editor.vue index 67d046f..e80ca11 100644 --- a/src/pages/scene-editor.vue +++ b/src/pages/scene-editor.vue @@ -100,7 +100,7 @@ const importScene = async () => { const file = await selectFile('.scene'); if (!file?.size) return; const json = await decodeTextFile(file); - editor.value?.load(json, editable.value); + editor.value?.load(json, editable.value, undefined, true); // 第四个参数isImport=true }; const exportScene = () => { const json = editor.value?.save(); diff --git a/src/services/editor.service.ts b/src/services/editor.service.ts index 5034a5a..575d555 100644 --- a/src/services/editor.service.ts +++ b/src/services/editor.service.ts @@ -45,8 +45,14 @@ export class EditorService extends Meta2d { * @param map 场景文件的JSON字符串,为空则创建新场景 * @param editable 是否可编辑状态,控制编辑器锁定状态 * @param detail 群组场景详情,包含机器人组和机器人信息 + * @param isImport 是否为导入场景文件,true时进行反向坐标转换 */ - public async load(map?: string, editable = false, detail?: Partial): Promise { + public async load( + map?: string, + editable = false, + detail?: Partial, + isImport = false, + ): Promise { const scene: StandardScene = map ? JSON.parse(map) : {}; if (!isEmpty(detail?.group)) { scene.robotGroups = [detail.group]; @@ -59,9 +65,9 @@ export class EditorService extends Meta2d { this.open(); this.setState(editable); this.#loadRobots(robotGroups, robots); - await this.#loadScenePoints(points); - this.#loadSceneRoutes(routes); - await this.#loadSceneAreas(areas); + await this.#loadScenePoints(points, isImport); + this.#loadSceneRoutes(routes, isImport); + await this.#loadSceneAreas(areas, isImport); this.store.historyIndex = undefined; this.store.histories = []; // this.scale(scale);与xd 自定义缩放冲突,暂时去掉 @@ -105,13 +111,22 @@ export class EditorService extends Meta2d { /** * 从场景数据加载点位到画布 * @param points 标准场景点位数据数组 + * @param isImport 是否为导入场景文件,true时进行反向坐标转换 */ - async #loadScenePoints(points?: StandardScenePoint[]): Promise { + async #loadScenePoints(points?: StandardScenePoint[], isImport = false): Promise { if (!points?.length) return; await Promise.all( points.map(async (v) => { const { id, name, desc, x, y, type, extensionType, robots, actions, properties, deviceId, enabled } = v; - await this.addPoint({ x, y }, type, id); + // 只有在导入场景文件时才进行反向坐标转换 + let finalX = x; + let finalY = y; + if (isImport) { + const transformedCoords = this.#reverseTransformCoordinate(x, y); + finalX = transformedCoords.x; + finalY = transformedCoords.y; + } + await this.addPoint({ x: finalX, y: finalY }, type, id); this.setValue( { id, label: name, desc, properties, point: { type, extensionType, robots, actions, deviceId, enabled } }, { render: false, history: false, doEvent: false }, @@ -122,8 +137,9 @@ export class EditorService extends Meta2d { /** * 从场景数据加载路线到画布 * @param routes 标准场景路线数据数组 + * @param isImport 是否为导入场景文件,true时进行反向坐标转换 */ - #loadSceneRoutes(routes?: StandardSceneRoute[]): void { + #loadSceneRoutes(routes?: StandardSceneRoute[], isImport = false): void { if (!routes?.length) return; routes.map((v) => { const { id, desc, from, to, type, pass, c1, c2, properties } = v; @@ -133,6 +149,23 @@ export class EditorService extends Meta2d { this.addRoute([p1, p2], type, id); const { x: x1, y: y1 } = this.getPointRect(p1)!; const { x: x2, y: y2 } = this.getPointRect(p2)!; + + // 只有在导入场景文件时才对控制点坐标进行反向转换 + let transformedC1 = { x: (c1?.x ?? 0) - x1, y: (c1?.y ?? 0) - y1 }; + let transformedC2 = { x: (c2?.x ?? 0) - x2, y: (c2?.y ?? 0) - y2 }; + + if (isImport) { + if (c1 && c1.x !== undefined && c1.y !== undefined) { + const reversedC1 = this.#reverseTransformCoordinate(c1.x, c1.y); + transformedC1 = { x: reversedC1.x - x1, y: reversedC1.y - y1 }; + } + + if (c2 && c2.x !== undefined && c2.y !== undefined) { + const reversedC2 = this.#reverseTransformCoordinate(c2.x, c2.y); + transformedC2 = { x: reversedC2.x - x2, y: reversedC2.y - y2 }; + } + } + this.setValue( { id, @@ -141,20 +174,39 @@ export class EditorService extends Meta2d { route: { type, pass, - c1: { x: (c1?.x ?? 0) - x1, y: (c1?.y ?? 0) - y1 }, - c2: { x: (c2?.x ?? 0) - x2, y: (c2?.y ?? 0) - y2 }, + c1: transformedC1, + c2: transformedC2, }, }, { render: false, history: false, doEvent: false }, ); }); } - async #loadSceneAreas(areas?: StandardSceneArea[]): Promise { + /** + * 从场景数据加载区域到画布 + * @param areas 标准场景区域数据数组 + * @param isImport 是否为导入场景文件,true时进行反向坐标转换 + */ + async #loadSceneAreas(areas?: StandardSceneArea[], isImport = false): Promise { if (!areas?.length) return; await Promise.all( areas.map(async (v) => { const { id, name, desc, x, y, w, h, type, points, routes, maxAmr, inoutflag, storageLocations, properties } = v; - await this.addArea({ x, y }, { x: x + w, y: y + h }, type, id); + // 只有在导入场景文件时才进行反向坐标转换 + let finalX = x; + let finalY = y; + let finalW = w; + let finalH = h; + + if (isImport) { + const transformedCoords = this.#reverseTransformCoordinate(x, y); + finalX = transformedCoords.x; + finalY = transformedCoords.y; + finalW = this.#reverseTransformSize(w); + finalH = this.#reverseTransformSize(h); + } + + await this.addArea({ x: finalX, y: finalY }, { x: finalX + finalW, y: finalY + finalH }, type, id); // 对于库区类型,需要将点位名称数组转换为点位ID数组,并更新动作点的库位信息 let processedPoints = points; @@ -503,6 +555,34 @@ export class EditorService extends Meta2d { return this.#fixPrecision(scaledSize); } + /** 反向坐标转换方法 - 将中心点原点的坐标转换为左上角原点的坐标 */ + #reverseTransformCoordinate(x: number, y: number): { x: number; y: number } { + const { ratio = 1, width = 0, height = 0 } = this.#originalSceneData ?? {}; + + // 先进行坐标系转换:中心点原点 -> 左上角原点 + const topLeftX = x + width / 2; + const topLeftY = height / 2 - y; + + // 再根据ratio进行缩放 + const scaledX = topLeftX * ratio; + const scaledY = topLeftY * ratio; + + // 应用精度控制:保留3位小数,之后直接舍去 + return { + x: this.#fixPrecision(scaledX), + y: this.#fixPrecision(scaledY), + }; + } + + /** 反向尺寸转换方法 - 根据ratio还原尺寸 */ + #reverseTransformSize(size: number): number { + const { ratio = 1 } = this.#originalSceneData ?? {}; + const scaledSize = size * ratio; + + // 应用精度控制:保留3位小数,之后直接舍去 + return this.#fixPrecision(scaledSize); + } + /** 精度控制方法 - 固定3位小数,3位之后直接舍去(不四舍五入),不足3位则补齐 */ #fixPrecision(value: number): number { // 先截断到3位小数(不四舍五入)