Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,831 changes: 0 additions & 2,831 deletions GEMstack/onboard/visualization/sr_viz/threeD/public/example.json

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React, { useEffect } from "react";
import { Environment } from "@react-three/drei";
import { useTimelineStore } from "@/hooks/useTimelineStore";
import Vehicle from "./Vehicle";
import Agent from "./Agent";
import Pedestrian from "./Pedestrian";
import TrafficLight from "./TrafficLight";
import OtherVehicle from "./OtherVehicle";
import Ground from "./Ground";
Expand All @@ -18,7 +18,7 @@ export default function CanvasWrapper({
time: number;
setDuration: (duration: number) => void;
}) {
const { vehicle, agents, trafficLights, trafficCones, otherVehicles } =
const { vehicle, pedestrians, trafficLights, trafficCones, otherVehicles } =
useTimelineStore();

useEffect(() => {
Expand All @@ -33,6 +33,8 @@ export default function CanvasWrapper({
return (
<Canvas
shadows
dpr={[1, 2]}
gl={{ antialias: true }}
camera={{ position: [0, 5, 15], fov: 55, near: 0.1, far: 100000 }}
style={{
background: "#fdfdfd",
Expand All @@ -46,8 +48,8 @@ export default function CanvasWrapper({
<directionalLight position={[10, 10, 5]} intensity={1.5} castShadow />
<Vehicle timeline={vehicle} time={syncedTime} />

{Object.entries(agents).map(([id, timeline]) => (
<Agent key={id} id={id} timeline={timeline} time={syncedTime} />
{Object.entries(pedestrians).map(([id, timeline]) => (
<Pedestrian key={id} id={id} timeline={timeline} time={syncedTime} />
))}

{Object.entries(trafficLights).map(([id, timeline]) => (
Expand All @@ -65,4 +67,4 @@ export default function CanvasWrapper({
<Ground />
</Canvas>
);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import React, { useEffect, useState } from "react";
import React, { useRef, useState, useEffect } from "react";
import { TiUpload } from "react-icons/ti";
import { RxCross2 } from "react-icons/rx";
import { buildTimeline } from "@/utils/buildTimeline";
Expand All @@ -21,33 +21,39 @@ export default function ControlPanel({
const [fileName, setFileName] = useState<string | null>(null);
const setTimeline = useTimelineStore((state) => state.setTimeline);

// ref so we can clear input.value after upload
const fileInputRef = useRef<HTMLInputElement>(null);

const handleFileUpload = async (
event: React.ChangeEvent<HTMLInputElement>
) => {
const file = event.target.files?.[0];
if (!file) return;
const uploaded = event.target.files?.[0];
if (!uploaded) return;

setFileName(file.name);
setFileName(uploaded.name);

try {
const entries = await parseLogFile(file);
const entries = await parseLogFile(uploaded);
const timeline: TimelineData = buildTimeline(entries);
setTimeline(timeline);
reset();
console.log("timeline loaded:", timeline);
} catch (err) {
console.error("Failed to parse log file:", err);
}

// reset the input so same filename can be re‐picked
if (fileInputRef.current) {
fileInputRef.current.value = "";
}
};

const handleContextMenu = (event: React.MouseEvent) => {
event.preventDefault();
if (isOpen) {
setIsOpen(false);
} else {
setIsOpen(true);
}
}
setIsOpen((o) => !o);
};

// auto‐load from Flask if folder+file props provided
useEffect(() => {
if (!folder || !file) return;

Expand All @@ -59,42 +65,38 @@ export default function ControlPanel({
try {
const res = await fetch(url);
if (!res.ok) {
throw new Error(
`Failed to fetch ${file} from ${url} (status ${res.status})`
);
throw new Error(`Failed to fetch ${file} (status ${res.status})`);
}

const text = await res.text();
const fakeFile = new File([text], file, { type: "text/plain" });

const entries = await parseLogFile(fakeFile);
const timeline = buildTimeline(entries);

setTimeline(timeline);
reset();
setFileName(file);

console.log("Timeline loaded from Flask API:", timeline);
} catch (err) {
console.error("Failed to load remote log file:", err);
}
};

fetchLog();
}, [folder, file]);
}, [folder, file, reset, setTimeline]);

return (
<>
<div
className={`fixed top-0 left-0 h-full w-64 bg-black/80 text-white shadow-lg transform transition-transform duration-500 ease-in-out z-40 ${
className={`fixed top-0 left-0 h-full w-48 max-w-[90vw] bg-black/80 text-white shadow-lg transform transition-transform duration-300 ease-in-out z-40 overflow-y-auto flex flex-col ${
isOpen ? "translate-x-0" : "-translate-x-full"
}`}
onContextMenu={handleContextMenu}
>
{isOpen && (
<>
<div className="flex items-center justify-between px-4 py-3 border-b border-white/20">
<h2 className="text-lg font-semibold">Control Panel</h2>
<h2 className="text-base font-semibold whitespace-nowrap">
Control Panel
</h2>
<button
onClick={() => setIsOpen(false)}
className="text-white p-1 rounded hover:bg-white/10"
Expand All @@ -109,14 +111,15 @@ export default function ControlPanel({
<TiUpload className="w-4 h-4" />
<span>Choose file</span>
<input
ref={fileInputRef}
type="file"
accept=".json,.txt,.log"
className="hidden"
onChange={handleFileUpload}
/>
</label>
<p className="mt-2 text-xs text-gray-400 truncate">
{fileName ? `${fileName}` : "No file loaded"}
{fileName ?? "No file loaded"}
</p>
</div>
</>
Expand All @@ -135,4 +138,4 @@ export default function ControlPanel({
)}
</>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ import { useFrame } from "@react-three/fiber";
import { Mesh, Object3D, MeshStandardMaterial } from "three";
import { useGLTF } from "@react-three/drei";
import { FrameData } from "@/types/FrameData";
import { currentAgent } from "@/config/agentConfig";
import { currentPedestrian } from "@/config/pedestrianConfig";

interface AgentProps {
interface PedestrianProps {
id: string;
timeline: FrameData[];
time: number;
}

export default function Agent({ id, timeline, time }: AgentProps) {
export default function Pedestrian({ id, timeline, time }: PedestrianProps) {
const [mounted, setMounted] = useState(false);

const ref = useRef<Mesh>(null);
const { modelPath, scale, rotation, offset, bodyColor } = currentAgent;
const { modelPath, scale, rotation, offset, bodyColor } = currentPedestrian;
const { scene } = useGLTF(modelPath);
const clonedScene = useMemo(() => scene.clone(true), [scene]);

Expand Down Expand Up @@ -59,4 +59,4 @@ export default function Agent({ id, timeline, time }: AgentProps) {
position={offset}
/>
);
}
}
Loading
Loading