Files
Mujoco_WASM/web_platform/src/map/mapSceneDraft.ts
T
chenlin a9b07e0abf
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
feat(web-platform): release V0.8.1 常规地图优化
2026-09-03 13:18:35 +08:00

122 lines
4.4 KiB
TypeScript

import type { EditableMapDocument } from './editor/types';
import { normalizePhysicalMapConfig } from './physicalMap';
import {
mapSelectionTransform,
updatePlacedMapAsset,
type MapInstanceTransform,
type PlacedMapAsset,
} from './types';
function sameAsset(left: PlacedMapAsset | undefined, right: PlacedMapAsset | undefined): boolean {
return Boolean(
left &&
right &&
left.id === right.id &&
left.name === right.name &&
JSON.stringify(left.selection) === JSON.stringify(right.selection),
);
}
function cloneAsset(asset: PlacedMapAsset): PlacedMapAsset {
return { ...asset, selection: structuredClone(asset.selection) };
}
export interface MapSceneDraftSummary {
dirty: boolean;
changeCount: number;
/** 当前仍存在且相对应用基线新增或发生修改的实例 ID。 */
changedIds: string[];
/** 可直接在 Three.js 中预览的新增或已修改程序地图。 */
previewAssets: PlacedMapAsset[];
}
export interface MapEditorDraftPreviewInstance {
id: string;
document: EditableMapDocument;
transform: MapInstanceTransform;
}
/**
* 当前激活的认证场景由交互编辑层绘制;其余待提交工程地图必须保留只读预览,
* 否则切换到参数地形时会因为清空交互文档而从画布消失。
*/
export function mapEditorDraftPreviewInstances(
assets: readonly PlacedMapAsset[],
pendingIds: readonly string[],
documents: ReadonlyMap<string, EditableMapDocument>,
activeId?: string,
): MapEditorDraftPreviewInstance[] {
const pending = new Set(pendingIds);
return assets.flatMap((asset) => {
if (asset.id === activeId || !pending.has(asset.id) || asset.selection.kind !== 'project')
return [];
const document = documents.get(asset.selection.descriptorPath);
return document
? [{ id: asset.id, document, transform: mapSelectionTransform(asset.selection) }]
: [];
});
}
/**
* 所有地图实例共享同一个场景草稿边界。新增、删除、替换和实例参数修改只更新
* `current`,显式应用成功后才推进 `applied` 基线。
*/
export function summarizeMapSceneDraft(
current: readonly PlacedMapAsset[],
applied: readonly PlacedMapAsset[],
): MapSceneDraftSummary {
const currentById = new Map(current.map((asset) => [asset.id, asset]));
const appliedById = new Map(applied.map((asset) => [asset.id, asset]));
const changed = current.filter((asset) => !sameAsset(asset, appliedById.get(asset.id)));
const removed = applied.filter((asset) => !currentById.has(asset.id));
return {
dirty: changed.length + removed.length > 0,
changeCount: changed.length + removed.length,
changedIds: changed.map((asset) => asset.id),
previewAssets: changed.filter((asset) => asset.selection.kind === 'builtin').map(cloneAsset),
};
}
/** 放弃完整场景草稿,严格恢复上一次成功编译的实例顺序和状态。 */
export function restoreAppliedMapScene(applied: readonly PlacedMapAsset[]): PlacedMapAsset[] {
return applied.map(cloneAsset);
}
export function clonePlacedMapAssets(assets: readonly PlacedMapAsset[]): PlacedMapAsset[] {
return assets.map(cloneAsset);
}
/** 普通模型重载使用应用基线;只有显式场景提交才传入候选实例。 */
export function resolveMapSceneLoadAssets(
applied: readonly PlacedMapAsset[],
requested?: readonly PlacedMapAsset[],
): PlacedMapAsset[] {
return clonePlacedMapAssets(requested ?? applied);
}
/** 视口操纵器只更新参数地形草稿,不触发 MuJoCo 编译。 */
export function transformParametricMapAsset(
assets: readonly PlacedMapAsset[],
id: string,
position: readonly [number, number],
yawDeg: number,
): PlacedMapAsset[] | readonly PlacedMapAsset[] {
const target = assets.find((asset) => asset.id === id);
if (!target || target.selection.kind !== 'builtin' || target.selection.config.preset === 'none')
return assets;
const clean = (value: number) => Math.round(value * 1_000_000) / 1_000_000;
const config = normalizePhysicalMapConfig({
...target.selection.config,
positionX: clean(position[0]),
positionY: clean(position[1]),
yawDeg: clean(yawDeg),
});
if (
config.positionX === target.selection.config.positionX &&
config.positionY === target.selection.config.positionY &&
config.yawDeg === target.selection.config.yawDeg
)
return assets;
return updatePlacedMapAsset(assets, id, { kind: 'builtin', config });
}