-
+
+
{currentTime < duration
? formatDuration(currentTime)
: formatDuration(duration)}
-
+
setIsDragging(true)}
onChangeCommitted={handleSliderChangeCommitted}
value={currentTime}
/>
- {formatDuration(duration)}
+ {formatDuration(duration)}
-
diff --git a/GEMstack/onboard/visualization/sr_viz/threeD/src/components/Vehicle.tsx b/GEMstack/onboard/visualization/sr_viz/threeD/src/components/Vehicle.tsx
index 5fabb99a1..a972f4a8a 100644
--- a/GEMstack/onboard/visualization/sr_viz/threeD/src/components/Vehicle.tsx
+++ b/GEMstack/onboard/visualization/sr_viz/threeD/src/components/Vehicle.tsx
@@ -33,8 +33,10 @@ export default function Vehicle({ timeline, time }: VehicleProps) {
robot.position.set(offset[0], offset[1], offset[2]);
robot.traverse((child) => {
- if (child instanceof THREE.Mesh &&
- child.material instanceof THREE.MeshStandardMaterial) {
+ if (
+ child instanceof THREE.Mesh &&
+ child.material instanceof THREE.MeshStandardMaterial
+ ) {
child.material = child.material.clone();
child.material.color.set(bodyColor);
}
@@ -72,4 +74,4 @@ export default function Vehicle({ timeline, time }: VehicleProps) {
{mode === "free" &&
}
>
);
-}
+}
\ No newline at end of file
diff --git a/GEMstack/onboard/visualization/sr_viz/threeD/src/components/VehicleInfoPanel.tsx b/GEMstack/onboard/visualization/sr_viz/threeD/src/components/VehicleInfoPanel.tsx
index 2d5dffd97..72ab56221 100644
--- a/GEMstack/onboard/visualization/sr_viz/threeD/src/components/VehicleInfoPanel.tsx
+++ b/GEMstack/onboard/visualization/sr_viz/threeD/src/components/VehicleInfoPanel.tsx
@@ -1,10 +1,11 @@
"use client";
+
import React, { useState } from "react";
import { useTimelineStore } from "@/hooks/useTimelineStore";
import { RxCross2 } from "react-icons/rx";
export function VehicleInfoPanel({ time }: { time: number }) {
- const [isOpen, setIsOpen] = useState(false);
+ const [isOpen, setIsOpen] = useState(true);
const frames = useTimelineStore((s) => s.vehicle);
const toggleOpen = (e?: React.MouseEvent) => {
@@ -19,55 +20,66 @@ export function VehicleInfoPanel({ time }: { time: number }) {
const current = frames.filter((f) => f.time <= currentTimestamp).pop();
return (
-
+ <>
- {isOpen && (
- <>
-
-
Current Vehicle State
-
-
- {current ? (
-
- -
- Timestamp: {current.time}
-
- -
- X: {current.x}
-
- -
- Y: {current.y}
-
- -
- Z: {current.z}
-
- -
- Yaw: {current.yaw}
-
- -
- Pitch: {current.pitch}
-
- -
- Roll: {current.roll}
+
+
Vehicle State
+
+
+
+ {/* Scrollable content */}
+
+ {current ? (
+
+ {[
+ ["Timestamp", current.time],
+ ["X", current.x],
+ ["Y", current.y],
+ ["Z", current.z],
+ ["Yaw", current.yaw],
+ ["Pitch", current.pitch],
+ ["Roll", current.roll],
+ ].map(([label, value]) => (
+ -
+ {label}:
+ {value}
-
- ) : (
-
No vehicle data at this time
- )}
- >
- )}
+ ))}
+
+ {current.metadata &&
+ Object.entries(current.metadata).map(([key, value]) => {
+ if (typeof value === "object") return null;
+ return (
+
-
+
+ {key.replace(/_/g, " ")}:
+
+
+ {String(value)}
+
+
+ );
+ })}
+
+ ) : (
+
+ No vehicle data at this time
+
+ )}
+
+
{!isOpen && (
)}
-
+ >
);
-}
+}
\ No newline at end of file
diff --git a/GEMstack/onboard/visualization/sr_viz/threeD/src/config/groundConfig.ts b/GEMstack/onboard/visualization/sr_viz/threeD/src/config/groundConfig.ts
index 751a789f7..914fc6362 100644
--- a/GEMstack/onboard/visualization/sr_viz/threeD/src/config/groundConfig.ts
+++ b/GEMstack/onboard/visualization/sr_viz/threeD/src/config/groundConfig.ts
@@ -17,16 +17,16 @@ const groundConfig: GroundConfig = {
size: [1000, 1000],
position: [0, 0, 0],
rotation: [0, 0, 0],
- cellSize: 1,
+ cellSize: 2,
cellThickness: 0.5,
- cellColor: "#000000",
+ cellColor: "#222222",
sectionSize: 10,
sectionThickness: 1,
- sectionColor: "#000000",
- fadeDistance: 100,
- fadeStrength: 1,
+ sectionColor: "#444444",
+ fadeDistance: 1500,
+ fadeStrength: 0,
infiniteGrid: false,
};
-export { groundConfig };
+export { groundConfig };
\ No newline at end of file
diff --git a/GEMstack/onboard/visualization/sr_viz/threeD/src/config/agentConfig.ts b/GEMstack/onboard/visualization/sr_viz/threeD/src/config/pedestrianConfig.ts
similarity index 63%
rename from GEMstack/onboard/visualization/sr_viz/threeD/src/config/agentConfig.ts
rename to GEMstack/onboard/visualization/sr_viz/threeD/src/config/pedestrianConfig.ts
index 7d64a400d..57030e97d 100644
--- a/GEMstack/onboard/visualization/sr_viz/threeD/src/config/agentConfig.ts
+++ b/GEMstack/onboard/visualization/sr_viz/threeD/src/config/pedestrianConfig.ts
@@ -1,4 +1,4 @@
-const agentConfig = {
+const pedestrianConfig = {
pedestrian: {
name: "Pedestrian",
modelPath: "/models/pedestrian.glb",
@@ -9,6 +9,6 @@ const agentConfig = {
}
};
-const currentAgent = agentConfig.pedestrian;
+const currentPedestrian = pedestrianConfig.pedestrian;
-export { agentConfig, currentAgent };
+export { pedestrianConfig, currentPedestrian };
\ No newline at end of file
diff --git a/GEMstack/onboard/visualization/sr_viz/threeD/src/hooks/useTimelineStore.ts b/GEMstack/onboard/visualization/sr_viz/threeD/src/hooks/useTimelineStore.ts
index 30df1e5df..ba371de9e 100644
--- a/GEMstack/onboard/visualization/sr_viz/threeD/src/hooks/useTimelineStore.ts
+++ b/GEMstack/onboard/visualization/sr_viz/threeD/src/hooks/useTimelineStore.ts
@@ -3,7 +3,7 @@ import { create } from 'zustand';
export const useTimelineStore = create
((set) => ({
vehicle: [],
- agents: {},
+ pedestrians: {},
trafficLights: {},
trafficCones: {},
otherVehicles: {},
diff --git a/GEMstack/onboard/visualization/sr_viz/threeD/src/types/FrameData.ts b/GEMstack/onboard/visualization/sr_viz/threeD/src/types/FrameData.ts
index 78ea540cd..8e9442962 100644
--- a/GEMstack/onboard/visualization/sr_viz/threeD/src/types/FrameData.ts
+++ b/GEMstack/onboard/visualization/sr_viz/threeD/src/types/FrameData.ts
@@ -6,4 +6,5 @@ export interface FrameData {
yaw: number;
pitch: number;
roll: number;
+ metadata?: Record;
}
\ No newline at end of file
diff --git a/GEMstack/onboard/visualization/sr_viz/threeD/src/types/TimelineData.ts b/GEMstack/onboard/visualization/sr_viz/threeD/src/types/TimelineData.ts
index 513ef27bc..8db52b621 100644
--- a/GEMstack/onboard/visualization/sr_viz/threeD/src/types/TimelineData.ts
+++ b/GEMstack/onboard/visualization/sr_viz/threeD/src/types/TimelineData.ts
@@ -2,7 +2,7 @@ import { FrameData } from './FrameData';
export interface TimelineData {
vehicle: FrameData[];
- agents: Record;
+ pedestrians: Record;
trafficLights: Record;
trafficCones: Record;
otherVehicles: Record;
diff --git a/GEMstack/onboard/visualization/sr_viz/threeD/src/utils/buildTimeline.ts b/GEMstack/onboard/visualization/sr_viz/threeD/src/utils/buildTimeline.ts
index 1c07caa7d..754c6865c 100644
--- a/GEMstack/onboard/visualization/sr_viz/threeD/src/utils/buildTimeline.ts
+++ b/GEMstack/onboard/visualization/sr_viz/threeD/src/utils/buildTimeline.ts
@@ -1,35 +1,36 @@
-import { LogEntry } from '@/types/LogEntry';
-import { FrameData } from '@/types/FrameData';
-import { TimelineData } from '@/types/TimelineData';
+import { LogEntry } from "@/types/LogEntry";
+import { FrameData } from "@/types/FrameData";
+import { TimelineData } from "@/types/TimelineData";
export function buildTimeline(entries: LogEntry[]): TimelineData {
const vehicle: FrameData[] = [];
- const agents: Record = {};
+ const pedestrians: Record = {};
const trafficLights: Record = {};
const trafficCones: Record = {};
const otherVehicles: Record = {};
for (const entry of entries) {
const pose = entry.data?.pose;
- if (!pose || typeof pose.x !== 'number' || typeof pose.y !== 'number') continue;
+ if (!pose || typeof pose.x !== "number" || typeof pose.y !== "number") continue;
const frame: FrameData = {
time: entry.time,
- x: pose.x,
- y: pose.y,
+ x: pose.x ?? 0,
+ y: pose.y ?? 0,
z: pose.z ?? 0,
yaw: pose.yaw ?? 0,
pitch: pose.pitch ?? 0,
- roll: pose.roll ?? 0
+ roll: pose.roll ?? 0,
+ metadata: entry.key === "vehicle" ? { ...entry.data } : undefined,
};
- if (entry.key === 'vehicle') {
+ if (entry.key === "vehicle") {
vehicle.push(frame);
- } else if (entry.type === 'AgentState') {
+ } else if (entry.type === "PedestrianState") {
const key = entry.key.trim();
- if (!agents[key]) agents[key] = [];
- agents[key].push(frame);
- } else if (entry.type === 'TrafficLightState') {
+ if (!pedestrians[key]) pedestrians[key] = [];
+ pedestrians[key].push(frame);
+ } else if (entry.type === "TrafficLightState") {
const key = entry.key.trim();
if (!trafficLights[key]) trafficLights[key] = [];
trafficLights[key].push(frame);
@@ -37,12 +38,12 @@ export function buildTimeline(entries: LogEntry[]): TimelineData {
const key = entry.key.trim();
if (!trafficCones[key]) trafficCones[key] = [];
trafficCones[key].push(frame);
- }else if (entry.type === 'OtherVehicleState') {
+ } else if (entry.type === "OtherVehicleState") {
const key = entry.key.trim();
if (!otherVehicles[key]) otherVehicles[key] = [];
otherVehicles[key].push(frame);
}
}
- return { vehicle, agents, trafficLights, trafficCones, otherVehicles };
+ return { vehicle, pedestrians, trafficLights, trafficCones, otherVehicles };
}
diff --git a/GEMstack/onboard/visualization/sr_viz/threeD/src/utils/parseLogFile.ts b/GEMstack/onboard/visualization/sr_viz/threeD/src/utils/parseLogFile.ts
index a742e8983..80cf98f81 100644
--- a/GEMstack/onboard/visualization/sr_viz/threeD/src/utils/parseLogFile.ts
+++ b/GEMstack/onboard/visualization/sr_viz/threeD/src/utils/parseLogFile.ts
@@ -12,7 +12,13 @@ export async function parseLogFile(file: File): Promise {
for (const [key, value] of Object.entries(parsed)) {
if (key === 'time' || typeof value !== 'object' || value === null) continue;
- if (key === 'vehicle' && 'type' in value && 'data' in value && (value as any).data?.pose?.frame === 3) {
+ // Main vehicle (frame 2)
+ if (
+ key === 'vehicle' &&
+ 'type' in value &&
+ 'data' in value &&
+ (value as any).data?.pose?.frame === 2
+ ) {
entries.push({
key,
type: (value as any).type,
@@ -22,20 +28,21 @@ export async function parseLogFile(file: File): Promise {
continue;
}
- if (key === 'agents' || key === 'traffic_lights' || key === 'traffic_cones' || key === 'other_vehicles') {
- for (const [itemId, itemValue] of Object.entries(value)) {
- if (typeof itemValue === 'object' && itemValue !== null && 'data' in itemValue) {
- const frame = (itemValue as any).data?.pose?.frame;
-
- if (key === 'agents' && frame !== 1) {
- continue;
- }
-
+ // Unified agents (pedestrians, lights, cones, others) — frame 0 only
+ if (key === 'agents') {
+ for (const [agentId, agentValue] of Object.entries(value)) {
+ if (
+ typeof agentValue === 'object' &&
+ agentValue !== null &&
+ 'data' in agentValue &&
+ (agentValue as any).data?.pose?.frame === 0
+ ) {
+ const inferredType = getTypeFromKey(agentId);
entries.push({
- key: itemId,
- type: (itemValue as any).type ?? guessTypeFromKey(key),
+ key: agentId,
+ type: inferredType,
time,
- data: (itemValue as any).data,
+ data: (agentValue as any).data,
});
}
}
@@ -49,10 +56,11 @@ export async function parseLogFile(file: File): Promise {
return entries;
}
-function guessTypeFromKey(key: string): string {
- if (key === 'agents') return 'AgentState';
- if (key === 'traffic_lights') return 'TrafficLightState';
- if (key === 'traffic_cones') return 'TrafficConeState';
- if (key === 'other_vehicles') return 'OtherVehicleState';
- return 'Unknown';
+function getTypeFromKey(key: string): string {
+ const lowerKey = key.toLowerCase();
+ if (lowerKey.startsWith('pedestrian')) return 'PedestrianState';
+ if (lowerKey.startsWith('vehicle')) return 'OtherVehicleState';
+ if (lowerKey.startsWith('light')) return 'TrafficLightState';
+ if (lowerKey.startsWith('cone')) return 'TrafficConeState';
+ return 'UnknownState';
}