import { useState } from 'react';
import {
BadgeCheck,
CheckCircle2,
Layers3,
MapPinned,
Mountain,
Plus,
RotateCcw,
Trash2,
Zap,
} from 'lucide-react';
import { Button, Select } from '../components/ui';
import {
CERTIFIED_MAP_ASSETS,
encodeMapLibraryDragPayload,
MAP_LIBRARY_DRAG_MIME,
} from './editor/assetCatalog';
import {
MAP_OBJECT_PLACEMENT_LABELS,
type EditableMapDocument,
type EditableMapObjectType,
type MapObjectPlacementMode,
} from './editor/types';
import type { MapEntry } from '../project/types';
import {
PHYSICAL_MAP_PRESET_LABELS,
SYSTEM_TERRAIN_PRESETS,
type PlacedMapAsset,
type SystemTerrainPreset,
} from './types';
function AssetPreview({ type, color }: { type: EditableMapObjectType; color: string }) {
const shape =
type === 'cylinder'
? 'h-9 w-9 rounded-full'
: type === 'capsule'
? 'h-10 w-6 rounded-full'
: type === 'ramp'
? 'h-0 w-0 border-b-[34px] border-l-[48px] border-l-transparent'
: type === 'stairs'
? 'h-9 w-11 [clip-path:polygon(0_100%,0_66%,34%_66%,34%_33%,67%_33%,67%_0,100%_0,100%_100%)]'
: 'h-9 w-9 rounded-sm';
return (
);
}
function TerrainPreview({ preset }: { preset: SystemTerrainPreset }) {
const grid = (
);
let shape;
if (preset === 'discrete_obstacles')
shape = (
);
else if (preset === 'gap')
shape = (
);
else if (preset === 'inverted_pyramid_stairs' || preset === 'pit')
shape = (
{preset === 'inverted_pyramid_stairs' && (
)}
);
else if (preset === 'pyramid_stairs')
shape = (
);
else if (preset === 'rails')
shape = (
);
else if (preset === 'rough')
shape = (
);
else if (preset === 'stepping_stones')
shape = (
);
else
shape = (
);
return (
);
}
function AddAction({ label }: { label: string }) {
return (
{label}
);
}
export function MapAssetLibrary({
disabled,
terrainSize,
document,
documents,
maps = [],
placedMaps = [],
activeMapId,
pendingSceneChangeCount = 0,
pendingSceneIds = [],
onAdd,
onAddProjectMap,
onSelectTerrain,
onApplyScene,
onDiscardScene,
onSelectMap,
onRemoveMap,
onSelectObject,
}: {
disabled: boolean;
terrainSize: number;
document?: EditableMapDocument | null;
documents?: ReadonlyMap;
maps?: MapEntry[];
placedMaps?: PlacedMapAsset[];
activeMapId?: string;
pendingSceneChangeCount?: number;
pendingSceneIds?: string[];
onAdd: (
type: EditableMapObjectType,
placementMode: MapObjectPlacementMode,
) => void | Promise;
onAddProjectMap?: (descriptorPath: string) => void;
onSelectTerrain: (preset: SystemTerrainPreset) => void;
onApplyScene?: () => void;
onDiscardScene?: () => void;
onSelectMap?: (id: string) => void;
onRemoveMap?: (id: string) => void;
onSelectObject?: (mapId: string, objectId: string) => void;
}) {
const [placementMode, setPlacementMode] = useState('auto_ground');
const pendingSceneIdSet = new Set(pendingSceneIds);
const placedObjectCount = placedMaps.reduce(
(total, map) =>
total +
(map.selection.kind === 'project'
? (documents?.get(map.selection.descriptorPath)?.objects.length ?? 0)
: 0),
0,
);
return (
已放置地图
{placedMaps.length} 个实例
{placedObjectCount ? ` · ${placedObjectCount} 个对象` : ''}
{placedMaps.length ? (
{placedMaps.map((map) => {
const active = map.id === activeMapId;
const mapDocument =
map.selection.kind === 'project'
? (documents?.get(map.selection.descriptorPath) ?? (active ? document : null))
: null;
return (
{mapDocument?.objects.length ? (
{mapDocument.objects.map((object) => (
))}
) : null}
);
})}
) : (
尚未放置地图。从下方工程地图、认证资产或参数地形添加,实例会在这里统一管理。
)}
{pendingSceneChangeCount > 0 && (
{pendingSceneChangeCount} 项场景更改待应用
工程地图、认证资产与参数地形共享同一草稿;继续编辑不会触发 MuJoCo 编译。
)}
{maps.length > 0 && (
工程地图源
点击添加或拖到画布定位;实例位姿独立,源内容由同源实例共享。
{maps.map((map) => (
{
if (disabled) {
event.preventDefault();
return;
}
event.dataTransfer.effectAllowed = 'copy';
event.dataTransfer.setData(
MAP_LIBRARY_DRAG_MIME,
encodeMapLibraryDragPayload({
kind: 'project',
descriptorPath: map.descriptorPath,
}),
);
event.dataTransfer.setData('text/plain', map.name);
}}
>
{map.name}
))}
)}
{CERTIFIED_MAP_ASSETS.map((asset) => (
{
if (disabled) {
event.preventDefault();
return;
}
event.dataTransfer.effectAllowed = 'copy';
event.dataTransfer.setData(
MAP_LIBRARY_DRAG_MIME,
encodeMapLibraryDragPayload({
kind: 'certified',
type: asset.type,
placementMode,
}),
);
event.dataTransfer.setData('text/plain', asset.name);
}}
>
{asset.name}
{asset.description}
))}
系统参数化地形
点击添加或拖到画布定位。新增先进入轻量预览,可连续放置后一次编译。
{SYSTEM_TERRAIN_PRESETS.map((preset) => (
{
if (disabled) {
event.preventDefault();
return;
}
event.dataTransfer.effectAllowed = 'copy';
event.dataTransfer.setData(
MAP_LIBRARY_DRAG_MIME,
encodeMapLibraryDragPayload({ kind: 'terrain', preset }),
);
event.dataTransfer.setData('text/plain', PHYSICAL_MAP_PRESET_LABELS[preset]);
}}
>
{PHYSICAL_MAP_PRESET_LABELS[preset]}
{terrainSize.toFixed(2)} × {terrainSize.toFixed(2)} m
))}
);
}