feat(simulation): optimize and persist collision viewing
This commit is contained in:
@@ -26,6 +26,7 @@ from k1link.simulation.gaussian_pipeline_gateway import (
|
||||
|
||||
PROJECT_SCHEMA: Final = "missioncore.simulation-project/v1"
|
||||
WORLD_MANIFEST_SCHEMA: Final = "missioncore.simulation-world-manifest/v1"
|
||||
VIEWER_SETTINGS_SCHEMA: Final = "missioncore.simulation-viewer-settings/v1"
|
||||
PROJECT_ID_PATTERN: Final = re.compile(r"^sim-[a-f0-9]{32}$")
|
||||
SOURCE_FILE_ID_PATTERN: Final = re.compile(r"^src-[0-9]{5}-[a-f0-9]{8}$")
|
||||
PROVIDER_JOB_ID_PATTERN: Final = re.compile(r"^gsp-[0-9]{14}-[a-f0-9]{8}$")
|
||||
@@ -142,6 +143,7 @@ class SimulationProjectStore:
|
||||
},
|
||||
"artifacts": [],
|
||||
"world_manifest": None,
|
||||
"viewer_settings": _default_viewer_settings(),
|
||||
"error": None,
|
||||
"created_at_utc": now,
|
||||
"updated_at_utc": now,
|
||||
@@ -189,6 +191,19 @@ class SimulationProjectStore:
|
||||
self._write(document)
|
||||
return document
|
||||
|
||||
def update_viewer_settings(
|
||||
self,
|
||||
project_id: str,
|
||||
settings: dict[str, Any],
|
||||
) -> dict[str, Any]:
|
||||
normalized = _viewer_settings(settings)
|
||||
with self._lock:
|
||||
document = self._read(project_id)
|
||||
document["viewer_settings"] = normalized
|
||||
document["updated_at_utc"] = utc_now_iso()
|
||||
self._write(document)
|
||||
return document
|
||||
|
||||
def upload_state(self, project_id: str, file_id: str) -> tuple[int, int]:
|
||||
with self._lock:
|
||||
document = self._read(project_id)
|
||||
@@ -239,7 +254,7 @@ class SimulationProjectStore:
|
||||
def begin_build(self, project_id: str) -> dict[str, Any]:
|
||||
with self._lock:
|
||||
document = self._read(project_id)
|
||||
if document["status"] not in {"uploading", "failed"}:
|
||||
if document["status"] not in {"uploading", "failed", "ready"}:
|
||||
raise SimulationProjectConflictError("simulation project cannot start a build")
|
||||
if any(
|
||||
int(item["uploaded_bytes"]) != int(item["byte_length"])
|
||||
@@ -371,6 +386,7 @@ class SimulationProjectStore:
|
||||
or document.get("project_id") != project_id
|
||||
):
|
||||
raise SimulationProjectError("persisted simulation project identity is invalid")
|
||||
document.setdefault("viewer_settings", _default_viewer_settings())
|
||||
return document
|
||||
|
||||
def _write(self, document: dict[str, Any]) -> None:
|
||||
@@ -421,7 +437,7 @@ class SimulationProjectService:
|
||||
|
||||
def begin_build(self, project_id: str) -> dict[str, Any]:
|
||||
project = self.store.get(project_id)
|
||||
if project.get("status") == "failed":
|
||||
if project.get("status") in {"failed", "ready"}:
|
||||
job_id = project["provider"].get("job_id")
|
||||
if isinstance(job_id, str):
|
||||
provider = self.provider_factory()
|
||||
@@ -616,7 +632,7 @@ def _world_manifest(project_id: str, artifacts: list[dict[str, Any]]) -> dict[st
|
||||
},
|
||||
"transforms": {
|
||||
"world_from_visual": [1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1],
|
||||
"world_from_collision": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
|
||||
"world_from_collision": [-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -628,11 +644,66 @@ def _collision_profile(scene_type: str) -> dict[str, Any]:
|
||||
"seed_position": [0, 1, 0],
|
||||
"capsule_height": 1.6,
|
||||
"capsule_radius": 0.2,
|
||||
"voxel_size": 0.05,
|
||||
"voxel_size": 0.2 if scene_type == "outdoor" else 0.1,
|
||||
"mesh_shape": "smooth",
|
||||
}
|
||||
|
||||
|
||||
def _default_viewer_settings() -> dict[str, Any]:
|
||||
return {
|
||||
"schema_version": VIEWER_SETTINGS_SCHEMA,
|
||||
"quality": "high",
|
||||
"visual": {"inverted": True, "axis": "x"},
|
||||
"collision": {"inverted": True, "axis": "y"},
|
||||
"camera": {
|
||||
"invert_horizontal": True,
|
||||
"invert_vertical": False,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _viewer_settings(value: object) -> dict[str, Any]:
|
||||
if not isinstance(value, dict) or set(value) != {
|
||||
"schema_version", "quality", "visual", "collision", "camera"
|
||||
}:
|
||||
raise SimulationProjectError("simulation viewer settings contract is invalid")
|
||||
if value.get("schema_version") != VIEWER_SETTINGS_SCHEMA:
|
||||
raise SimulationProjectError("simulation viewer settings version is unsupported")
|
||||
quality = value.get("quality")
|
||||
if quality not in {"low", "medium", "high", "ultra", "maximum"}:
|
||||
raise SimulationProjectError("simulation viewer quality is invalid")
|
||||
|
||||
def layer(name: str) -> dict[str, Any]:
|
||||
raw = value.get(name)
|
||||
if not isinstance(raw, dict) or set(raw) != {"inverted", "axis"}:
|
||||
raise SimulationProjectError(f"simulation {name} transform is invalid")
|
||||
inverted = raw.get("inverted")
|
||||
axis = raw.get("axis")
|
||||
if not isinstance(inverted, bool) or axis not in {"x", "y", "z"}:
|
||||
raise SimulationProjectError(f"simulation {name} transform is invalid")
|
||||
return {"inverted": inverted, "axis": axis}
|
||||
|
||||
camera = value.get("camera")
|
||||
if not isinstance(camera, dict) or set(camera) != {
|
||||
"invert_horizontal", "invert_vertical"
|
||||
}:
|
||||
raise SimulationProjectError("simulation camera settings are invalid")
|
||||
horizontal = camera.get("invert_horizontal")
|
||||
vertical = camera.get("invert_vertical")
|
||||
if not isinstance(horizontal, bool) or not isinstance(vertical, bool):
|
||||
raise SimulationProjectError("simulation camera settings are invalid")
|
||||
return {
|
||||
"schema_version": VIEWER_SETTINGS_SCHEMA,
|
||||
"quality": quality,
|
||||
"visual": layer("visual"),
|
||||
"collision": layer("collision"),
|
||||
"camera": {
|
||||
"invert_horizontal": horizontal,
|
||||
"invert_vertical": vertical,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _project_name(value: str) -> str:
|
||||
normalized = " ".join(value.split())
|
||||
if not 1 <= len(normalized) <= 120:
|
||||
|
||||
Reference in New Issue
Block a user