diff --git a/frontend/src/components/files/FileRenderer.tsx b/frontend/src/components/files/FileRenderer.tsx index 60299307..6fb36407 100644 --- a/frontend/src/components/files/FileRenderer.tsx +++ b/frontend/src/components/files/FileRenderer.tsx @@ -14,6 +14,7 @@ const FileRenderer: React.FC<{ ); case "stl": diff --git a/frontend/src/components/files/URDFRenderer.tsx b/frontend/src/components/files/URDFRenderer.tsx index 6199299b..43ab7468 100644 --- a/frontend/src/components/files/URDFRenderer.tsx +++ b/frontend/src/components/files/URDFRenderer.tsx @@ -17,8 +17,6 @@ import { UntarredFile } from "./Tarfile"; type Orientation = "Z-up" | "Y-up" | "X-up"; -type VisualizationTheme = "default" | "terminal" | "dark"; - interface JointControl { name: string; min: number; @@ -31,20 +29,22 @@ interface URDFInfo { linkCount: number; } +type Theme = "default" | "dark"; + interface Props { urdfContent: string; files: UntarredFile[]; useControls?: boolean; - visualTheme?: VisualizationTheme | null; showWireframe?: boolean; + supportedThemes?: Theme[]; } const URDFRenderer = ({ urdfContent, files, useControls = true, - visualTheme = null, showWireframe = false, + supportedThemes = ["default", "dark"], }: Props) => { const containerRef = useRef(null); const sceneRef = useRef(null); @@ -63,51 +63,8 @@ const URDFRenderer = ({ const rendererRef = useRef(null); const [isWireframe, setIsWireframe] = useState(showWireframe); const wireframeStateRef = useRef(showWireframe); - const [isDarkBackground, setIsDarkBackground] = useState(true); - const darkBackgroundStateRef = useRef(true); - - const applyTheme = useCallback((theme: VisualizationTheme) => { - if (!sceneRef.current) return; - const scene = sceneRef.current; - - switch (theme) { - case "terminal": - scene.background = new THREE.Color(0x000000); - robotRef.current?.traverse((child) => { - if (child instanceof THREE.Mesh) { - child.material = new THREE.MeshBasicMaterial({ - color: 0x00aa00, - wireframe: true, - wireframeLinewidth: 1, - }); - } - }); - break; - - case "dark": - scene.background = new THREE.Color(0x222222); - robotRef.current?.traverse((child) => { - if (child instanceof THREE.Mesh) { - child.material = new THREE.MeshPhongMaterial({ - color: 0x4444ff, - emissive: 0x000044, - }); - } - }); - break; - - default: - scene.background = new THREE.Color(0xf0f0f0); - robotRef.current?.traverse((child) => { - if (child instanceof THREE.Mesh) { - child.material = new THREE.MeshPhongMaterial({ - color: 0xaaaaaa, - }); - } - }); - break; - } - }, []); + const [theme, setTheme] = useState(() => supportedThemes[0]); + const themeRef = useRef("default"); useEffect(() => { const handleFullScreenChange = () => { @@ -121,7 +78,7 @@ const URDFRenderer = ({ value: joint.value, })); wireframeStateRef.current = isWireframe; - darkBackgroundStateRef.current = isDarkBackground; + themeRef.current = theme; setIsFullScreen(false); setForceRerender((prev) => prev + 1); @@ -131,17 +88,32 @@ const URDFRenderer = ({ document.addEventListener("fullscreenchange", handleFullScreenChange); return () => document.removeEventListener("fullscreenchange", handleFullScreenChange); - }, [jointControls, isWireframe, isDarkBackground]); + }, [jointControls, isWireframe, theme]); + + const getThemeColors = useCallback(() => { + switch (theme) { + case "default": + return { + background: "bg-[#f0f0f0]", + text: "text-gray-800", + backgroundColor: 0xf0f0f0, + }; + case "dark": + return { + background: "bg-black", + text: "text-gray-200", + backgroundColor: 0x000000, + }; + } + }, [theme]); useEffect(() => { if (!containerRef.current) return; const scene = new THREE.Scene(); sceneRef.current = scene; - scene.background = new THREE.Color( - darkBackgroundStateRef.current ? 0x000000 : 0xf0f0f0, - ); - setIsDarkBackground(darkBackgroundStateRef.current); + const colors = getThemeColors(); + scene.background = new THREE.Color(colors.backgroundColor); const camera = new THREE.PerspectiveCamera( 50, @@ -272,11 +244,6 @@ const URDFRenderer = ({ } }); - // Apply visual theme if specified - if (visualTheme !== null) { - applyTheme(visualTheme); - } - const animate = () => { requestAnimationFrame(animate); controls.update(); @@ -325,7 +292,7 @@ const URDFRenderer = ({ window.removeEventListener("resize", handleResize); updateOrientation("Z-up"); // Reset orientation on unmount }; - }, [urdfContent, files, orientation, forceRerender]); + }, [urdfContent, files, orientation, forceRerender, getThemeColors]); const handleJointChange = (index: number, value: number) => { setJointControls((prevControls) => { @@ -420,18 +387,6 @@ const URDFRenderer = ({ }); }, []); - // Add this helper function before the return statement - const getOrientationButtonColors = (theme: VisualizationTheme | null) => { - switch (theme) { - case "terminal": - return "bg-green-600 hover:bg-green-700"; - case "dark": - return "bg-blue-600 hover:bg-blue-700"; - default: - return "bg-purple-500 hover:bg-purple-600"; - } - }; - const resetViewerState = useCallback(() => { if ( !containerRef.current || @@ -498,108 +453,44 @@ const URDFRenderer = ({ } }, [isCycling]); - const toggleBackground = useCallback(() => { - if (sceneRef.current) { - setIsDarkBackground((prev) => { - const newIsDark = !prev; - sceneRef.current!.background = new THREE.Color( - newIsDark ? 0x000000 : 0xf0f0f0, - ); - return newIsDark; + const cycleTheme = useCallback(() => { + if (sceneRef.current && supportedThemes.length > 1) { + setTheme((prev) => { + const currentIndex = supportedThemes.indexOf(prev); + const nextIndex = (currentIndex + 1) % supportedThemes.length; + const newTheme = supportedThemes[nextIndex]; + + const colors = getThemeColors(); + sceneRef.current!.background = new THREE.Color(colors.backgroundColor); + themeRef.current = newTheme; + return newTheme; }); } - }, []); - - const getBackgroundColor = useCallback(() => { - return isDarkBackground ? "bg-black" : "bg-[#f0f0f0]"; - }, [isDarkBackground]); + }, [getThemeColors, supportedThemes]); useEffect(() => { - if (robotRef.current) { - robotRef.current.traverse((child) => { - if (child instanceof THREE.Mesh) { - child.material.wireframe = wireframeStateRef.current; - child.material.needsUpdate = true; - } - }); - } - }, [forceRerender]); - - useEffect(() => { - if (robotRef.current) { - robotRef.current.traverse((child) => { - if (child instanceof THREE.Mesh) { - child.material.wireframe = isWireframe; - child.material.needsUpdate = true; - } - }); + if (sceneRef.current) { + const colors = getThemeColors(); + sceneRef.current.background = new THREE.Color(colors.backgroundColor); } - wireframeStateRef.current = isWireframe; - }, [isWireframe]); + }, [theme, getThemeColors]); useEffect(() => { if (robotRef.current) { robotRef.current.traverse((child) => { if (child instanceof THREE.Mesh) { - if (!child.userData.originalColor) { - child.userData.originalColor = child.material.color.clone(); - } - const material = new THREE.MeshStandardMaterial({ - color: darkBackgroundStateRef.current - ? 0x00aa00 - : child.userData.originalColor, + color: child.userData.originalColor || child.material.color, metalness: 0.4, roughness: 0.6, wireframe: isWireframe, - emissive: darkBackgroundStateRef.current ? 0x00aa00 : 0x000000, - emissiveIntensity: darkBackgroundStateRef.current ? 0.5 : 0, }); child.material = material; child.material.needsUpdate = true; } }); } - }, [forceRerender]); - - useEffect(() => { - if (sceneRef.current) { - const backgroundColor = isDarkBackground ? 0x000000 : 0xf0f0f0; - sceneRef.current.background = new THREE.Color(backgroundColor); - darkBackgroundStateRef.current = isDarkBackground; - } - }, [isDarkBackground]); - - useEffect(() => { - if (!robotRef.current || !sceneRef.current) return; - - const robot = robotRef.current; - robot.rotation.set(0, 0, 0); - - switch (orientation) { - case "Y-up": - robot.rotateX(-Math.PI / 2); - break; - case "X-up": - robot.rotateZ(Math.PI / 2); - break; - } - - robot.traverse((child) => { - if (child instanceof THREE.Mesh) { - const material = new THREE.MeshStandardMaterial({ - color: isDarkBackground ? 0x00aa00 : child.userData.originalColor, - metalness: 0.4, - roughness: 0.6, - wireframe: isWireframe, - emissive: isDarkBackground ? 0x00aa00 : 0x000000, - emissiveIntensity: isDarkBackground ? 0.5 : 0, - }); - child.material = material; - child.material.needsUpdate = true; - } - }); - }, [orientation, isDarkBackground, isWireframe]); + }, [isWireframe]); return (
- {visualTheme !== "terminal" && ( + {supportedThemes.length > 1 && ( )}
@@ -643,7 +540,9 @@ const URDFRenderer = ({ {useControls && showControls && (
-
+