fix(control-station): align lidar viewer and expose polygon
This commit is contained in:
@@ -47,7 +47,6 @@ import type {
|
||||
import { useRecordedSessionAdmission } from "./core/observation/useRecordedSessionAdmission";
|
||||
import { useWorkspaceLayoutProfile } from "./core/observation/useWorkspaceLayoutProfile";
|
||||
import { resolvePolygonRunRoute } from "./core/polygon/runArchive";
|
||||
import { usePolygonWorkerCapability } from "./core/polygon/usePolygonWorkerCapability";
|
||||
import {
|
||||
OBSERVATION_WORKSPACE_ID,
|
||||
OBSERVATION_WORKSPACE_LAYOUT_VERSION,
|
||||
@@ -55,7 +54,7 @@ import {
|
||||
} from "./core/observation/workspaceLayout";
|
||||
import {
|
||||
rootById,
|
||||
rootsForCapabilities,
|
||||
roots,
|
||||
workspaceById,
|
||||
workspacesForRoot,
|
||||
type RootId,
|
||||
@@ -151,7 +150,6 @@ function mergeViewerSettings(
|
||||
export default function App() {
|
||||
const runtime = useMissionRuntime();
|
||||
const { selection } = useDevicePluginHost();
|
||||
const polygonWorker = usePolygonWorkerCapability();
|
||||
const polygonRunRoute = useMemo(
|
||||
() => resolvePolygonRunRoute(typeof window === "undefined" ? "" : window.location.search),
|
||||
[],
|
||||
@@ -199,10 +197,7 @@ export default function App() {
|
||||
const polygonRunRouteOpenedRef = useRef(false);
|
||||
|
||||
const currentRoot = rootById(activeRoot);
|
||||
const visibleRoots = useMemo(
|
||||
() => rootsForCapabilities({ polygonWorkerAvailable: polygonWorker.available }),
|
||||
[polygonWorker.available],
|
||||
);
|
||||
const visibleRoots = roots;
|
||||
const activeDefinition = workspaceById(workspace.activeView);
|
||||
const spatialWorkspaceActive = Boolean(
|
||||
workspace.contentOpen && activeDefinition?.kind === "spatial",
|
||||
|
||||
@@ -796,14 +796,6 @@ export function rootById(id: RootId | null): RootDefinition | null {
|
||||
return id ? roots.find((root) => root.id === id) ?? null : null;
|
||||
}
|
||||
|
||||
export function rootsForCapabilities({
|
||||
polygonWorkerAvailable,
|
||||
}: {
|
||||
polygonWorkerAvailable: boolean;
|
||||
}): RootDefinition[] {
|
||||
return roots.filter((root) => root.id !== "polygon" || polygonWorkerAvailable);
|
||||
}
|
||||
|
||||
export function workspaceById(id: string | null): WorkspaceDefinition | null {
|
||||
return id ? workspaces.find((workspace) => workspace.id === id) ?? null : null;
|
||||
}
|
||||
|
||||
@@ -1441,9 +1441,8 @@
|
||||
.lidar-field-cloud {
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
border: 1px solid rgb(255 255 255 / 0.09);
|
||||
border-radius: 0.9rem;
|
||||
background: #071018;
|
||||
background: #06070a;
|
||||
}
|
||||
|
||||
.lidar-field-camera {
|
||||
@@ -1461,7 +1460,6 @@
|
||||
.lidar-field-camera > .lidar-field-stage__label,
|
||||
.lidar-field-cloud > header {
|
||||
min-height: 3.45rem;
|
||||
border-bottom: 1px solid rgb(255 255 255 / 0.08);
|
||||
padding: 0.66rem 0.72rem;
|
||||
}
|
||||
|
||||
@@ -1487,7 +1485,6 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 0.5rem;
|
||||
border-top: 1px solid rgb(255 255 255 / 0.08);
|
||||
padding: 0.52rem 0.7rem;
|
||||
}
|
||||
|
||||
@@ -1548,7 +1545,7 @@
|
||||
overflow: hidden;
|
||||
margin-top: 0.8rem;
|
||||
border-radius: 0.9rem;
|
||||
background: #071018;
|
||||
background: #06070a;
|
||||
}
|
||||
|
||||
.lidar-fallback-review > header {
|
||||
@@ -1557,7 +1554,6 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.8rem;
|
||||
border-bottom: 1px solid rgb(255 255 255 / 0.08);
|
||||
padding: 0.65rem 0.75rem;
|
||||
}
|
||||
|
||||
@@ -1569,7 +1565,6 @@
|
||||
}
|
||||
|
||||
.lidar-fallback-review > footer {
|
||||
border-top: 1px solid rgb(255 255 255 / 0.08);
|
||||
padding: 0.58rem 0.75rem;
|
||||
color: var(--nodedc-text-muted);
|
||||
font-size: 0.58rem;
|
||||
@@ -1646,11 +1641,8 @@
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
margin-top: 0.85rem;
|
||||
border: 1px solid rgb(74 215 255 / 0.18);
|
||||
border-radius: 1rem;
|
||||
background:
|
||||
radial-gradient(circle at 18% 0%, rgb(56 124 255 / 0.12), transparent 34%),
|
||||
rgb(4 12 19 / 0.72);
|
||||
background: #06070a;
|
||||
padding: 0.85rem;
|
||||
}
|
||||
|
||||
@@ -1716,9 +1708,9 @@
|
||||
.lidar-ground-modes button,
|
||||
.lidar-ground-frame-control button,
|
||||
.lidar-ground-scene__toolbar button {
|
||||
border: 1px solid var(--station-hairline);
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
background: rgb(255 255 255 / 0.035);
|
||||
background: transparent;
|
||||
padding: 0.4rem 0.62rem;
|
||||
color: var(--nodedc-text-secondary);
|
||||
font: inherit;
|
||||
@@ -1730,9 +1722,16 @@
|
||||
.lidar-ground-modes button[data-active="true"],
|
||||
.lidar-ground-frame-control button:hover:not(:disabled),
|
||||
.lidar-ground-scene__toolbar button:hover {
|
||||
border-color: rgb(74 215 255 / 0.48);
|
||||
background: rgb(74 215 255 / 0.1);
|
||||
background: rgb(255 255 255 / 0.11);
|
||||
color: var(--nodedc-text-primary);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.lidar-ground-modes button:focus-visible,
|
||||
.lidar-ground-frame-control button:focus-visible,
|
||||
.lidar-ground-scene__toolbar button:focus-visible {
|
||||
background: rgb(255 255 255 / 0.14);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.lidar-ground-frame-control button {
|
||||
@@ -1751,16 +1750,15 @@
|
||||
|
||||
.lidar-ground-frame-control input {
|
||||
width: min(16rem, 24vw);
|
||||
accent-color: #4ad7ff;
|
||||
accent-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.lidar-ground-scene {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-height: 30rem;
|
||||
border: 1px solid rgb(255 255 255 / 0.09);
|
||||
border-radius: 0.9rem;
|
||||
background: #071018;
|
||||
background: #06070a;
|
||||
}
|
||||
|
||||
.lidar-ground-scene__viewport {
|
||||
@@ -1782,15 +1780,15 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
border: 1px solid rgb(255 255 255 / 0.08);
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
background: rgb(5 13 21 / 0.82);
|
||||
background: rgb(10 11 14 / 0.78);
|
||||
padding: 0.28rem;
|
||||
backdrop-filter: blur(14px);
|
||||
}
|
||||
|
||||
.lidar-ground-scene__toolbar button {
|
||||
background: rgb(74 215 255 / 0.08);
|
||||
background: rgb(255 255 255 / 0.06);
|
||||
}
|
||||
|
||||
.lidar-ground-scene__error {
|
||||
@@ -1808,9 +1806,8 @@
|
||||
place-content: center;
|
||||
justify-items: center;
|
||||
gap: 0.55rem;
|
||||
border: 1px solid var(--station-hairline);
|
||||
border-radius: 0.9rem;
|
||||
background: #071018;
|
||||
background: #06070a;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,12 +60,13 @@ function frameColors(
|
||||
const candidateAssigned = frame.masks.candidateAssigned[index] === 1;
|
||||
if (mode === "intensity") {
|
||||
const intensity = (frame.intensity0To255?.[index] ?? 96) / 255;
|
||||
const neutral = 0.16 + intensity * 0.8;
|
||||
setRgb(
|
||||
colors,
|
||||
offset,
|
||||
0.12 + intensity * 0.74,
|
||||
0.24 + intensity * 0.68,
|
||||
0.34 + intensity * 0.6,
|
||||
neutral,
|
||||
neutral,
|
||||
neutral,
|
||||
);
|
||||
} else if (mode === "current") {
|
||||
setRgb(
|
||||
@@ -131,7 +132,7 @@ export function LidarGroundPointCloud({
|
||||
}
|
||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
||||
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
||||
renderer.setClearColor(0x071018, 0.96);
|
||||
renderer.setClearColor(0x06070a, 1);
|
||||
renderer.domElement.setAttribute(
|
||||
"aria-label",
|
||||
"Интерактивное облако LiDAR",
|
||||
@@ -139,7 +140,7 @@ export function LidarGroundPointCloud({
|
||||
host.prepend(renderer.domElement);
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
const fog = new THREE.FogExp2(0x071018, 0.035);
|
||||
const fog = new THREE.FogExp2(0x06070a, 0.035);
|
||||
scene.fog = fog;
|
||||
fogRef.current = fog;
|
||||
const camera = new THREE.PerspectiveCamera(48, 1, 0.01, 1_000);
|
||||
@@ -172,7 +173,7 @@ export function LidarGroundPointCloud({
|
||||
materialRef.current = material;
|
||||
scene.add(new THREE.Points(geometry, material));
|
||||
|
||||
const grid = new THREE.GridHelper(10, 40, 0x71963d, 0x26312a);
|
||||
const grid = new THREE.GridHelper(10, 40, 0x454846, 0x252725);
|
||||
gridRef.current = grid;
|
||||
const gridMaterials = Array.isArray(grid.material)
|
||||
? grid.material
|
||||
|
||||
@@ -378,7 +378,7 @@ export function PolygonRoverScene({
|
||||
<span>
|
||||
{workerAvailable
|
||||
? "Запустите PX4/Gazebo — модель останется доступна для осмотра."
|
||||
: "Полигон появится в шапке после подтверждения Simulation Worker."}
|
||||
: "Прогоны и датасеты доступны; запуск и управление откроются после подтверждения Simulation Worker."}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -21,7 +21,7 @@ let stopPolygonWorker;
|
||||
let PolygonWorkerContractError;
|
||||
let workspaceById;
|
||||
let workspacesForRoot;
|
||||
let rootsForCapabilities;
|
||||
let roots;
|
||||
|
||||
before(async () => {
|
||||
server = await createServer({
|
||||
@@ -51,7 +51,7 @@ before(async () => {
|
||||
({
|
||||
workspaceById,
|
||||
workspacesForRoot,
|
||||
rootsForCapabilities,
|
||||
roots,
|
||||
} = await server.ssrLoadModule("/src/productModel.ts"));
|
||||
});
|
||||
|
||||
@@ -290,7 +290,7 @@ function commandAcceptance(overrides = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
test("Polygon is a worker-gated product root and keeps a compatible direct route", () => {
|
||||
test("Polygon stays visible while worker capability gates live actions", () => {
|
||||
assert.deepEqual(resolvePolygonRunRoute("?workspace=polygon-run"), {
|
||||
active: true,
|
||||
runId: null,
|
||||
@@ -320,16 +320,7 @@ test("Polygon is a worker-gated product root and keeps a compatible direct route
|
||||
workspacesForRoot("system").some(({ id }) => id === "polygon-run"),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
rootsForCapabilities({ polygonWorkerAvailable: false })
|
||||
.some(({ id }) => id === "polygon"),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
rootsForCapabilities({ polygonWorkerAvailable: true })
|
||||
.some(({ id }) => id === "polygon"),
|
||||
true,
|
||||
);
|
||||
assert.equal(roots.some(({ id }) => id === "polygon"), true);
|
||||
});
|
||||
|
||||
test("Polygon contracts decode path-free evidence and preserve the safety boundary", () => {
|
||||
|
||||
Reference in New Issue
Block a user