feat(web-platform): release V0.8.1 常规地图优化
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
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 });
|
||||
}
|
||||
Reference in New Issue
Block a user