jurmy24's picture
fix: roll back URDF changes and fix only what is needed
41b4066
raw
history blame
5.55 kB
import React, { useEffect, useRef, useState, useMemo } from "react";
import { cn } from "@/lib/utils";
import { useTheme } from "@/hooks/useTheme";
import URDFManipulator from "urdf-loader/src/urdf-manipulator-element.js";
import { useUrdf } from "@/hooks/useUrdf";
import {
createUrdfViewer,
setupMeshLoader,
setupJointHighlighting,
setupModelLoading,
} from "@/lib/urdfViewerHelpers";
import { ModeToggle } from "./ModeToggle";
// Register the URDFManipulator as a custom element if it hasn't been already
if (typeof window !== "undefined" && !customElements.get("urdf-viewer")) {
customElements.define("urdf-viewer", URDFManipulator);
}
// Extend the interface for the URDF viewer element to include background property
interface UrdfViewerElement extends HTMLElement {
background?: string;
setJointValue?: (jointName: string, value: number) => void;
}
const UrdfViewer: React.FC = () => {
const { theme } = useTheme();
const isDarkMode = theme === "dark";
const containerRef = useRef<HTMLDivElement>(null);
const [highlightedJoint, setHighlightedJoint] = useState<string | null>(null);
const { registerUrdfProcessor, alternativeUrdfModels, isDefaultModel } =
useUrdf();
// Add state for animation control
useState<boolean>(isDefaultModel);
const cleanupAnimationRef = useRef<(() => void) | null>(null);
const viewerRef = useRef<UrdfViewerElement | null>(null);
const hasInitializedRef = useRef<boolean>(false);
// Add state for custom URDF path
const [customUrdfPath, setCustomUrdfPath] = useState<string | null>(null);
const [urlModifierFunc, setUrlModifierFunc] = useState<
((url: string) => string) | null
>(null);
const packageRef = useRef<string>("");
// Implement UrdfProcessor interface for drag and drop
const urdfProcessor = useMemo(
() => ({
loadUrdf: (urdfPath: string) => {
setCustomUrdfPath(urdfPath);
},
setUrlModifierFunc: (func: (url: string) => string) => {
setUrlModifierFunc(() => func);
},
getPackage: () => {
return packageRef.current;
},
}),
[]
);
// Register the URDF processor with the global drag and drop context
useEffect(() => {
registerUrdfProcessor(urdfProcessor);
}, [registerUrdfProcessor, urdfProcessor]);
// Main effect to create and setup the viewer only once
useEffect(() => {
if (!containerRef.current) return;
// Create and configure the URDF viewer element
const viewer = createUrdfViewer(containerRef.current, isDarkMode);
viewerRef.current = viewer; // Store reference to the viewer
// Setup mesh loading function
setupMeshLoader(viewer, urlModifierFunc);
// Determine which URDF to load
// const urdfPath = isDefaultModel
// ? "/urdf/SO_5DOF_ARM100_05d/urdf/SO_5DOF_ARM100_05d.urdf"
// : customUrdfPath || "";
const urdfPath = isDefaultModel
? "/urdf/T12/urdf/T12.URDF"
: customUrdfPath || "";
// Setup model loading if a path is available
let cleanupModelLoading = () => {};
if (urdfPath) {
cleanupModelLoading = setupModelLoading(
viewer,
urdfPath,
packageRef.current,
setCustomUrdfPath,
alternativeUrdfModels
);
}
// Setup joint highlighting
const cleanupJointHighlighting = setupJointHighlighting(
viewer,
setHighlightedJoint
);
// Setup animation event handler for the default model or when hasAnimation is true
const onModelProcessed = () => {
hasInitializedRef.current = true;
if ("setJointValue" in viewer) {
// Clear any existing animation
if (cleanupAnimationRef.current) {
cleanupAnimationRef.current();
cleanupAnimationRef.current = null;
}
}
};
viewer.addEventListener("urdf-processed", onModelProcessed);
// Return cleanup function
return () => {
if (cleanupAnimationRef.current) {
cleanupAnimationRef.current();
cleanupAnimationRef.current = null;
}
hasInitializedRef.current = false;
cleanupJointHighlighting();
cleanupModelLoading();
viewer.removeEventListener("urdf-processed", onModelProcessed);
};
}, [isDefaultModel, customUrdfPath, urlModifierFunc]);
// Separate effect to handle theme changes without recreating the viewer
useEffect(() => {
if (!viewerRef.current) return;
// Update only the visual aspects based on theme
if (viewerRef.current.background !== undefined) {
if (isDarkMode) {
viewerRef.current.background = "#1f2937"; // Dark background
} else {
viewerRef.current.background = "#e0e7ff"; // Light background
}
}
}, [isDarkMode]);
return (
<div
className={cn(
"w-full h-full transition-all duration-300 ease-in-out relative",
isDarkMode
? "bg-gradient-to-br from-gray-900 to-gray-800"
: "bg-gradient-to-br from-blue-50 to-indigo-50"
)}
>
<div ref={containerRef} className="w-full h-full" />
{/* Control buttons container in top right */}
<div className="absolute top-4 right-4 flex items-center space-x-2 z-10">
{/* ModeToggle button */}
<ModeToggle />
</div>
{/* Joint highlight indicator */}
{highlightedJoint && (
<div className="absolute bottom-4 right-4 bg-black/70 text-white px-3 py-2 rounded-md text-sm font-mono z-10">
Joint: {highlightedJoint}
</div>
)}
</div>
);
};
export default UrdfViewer;