Skip to content
Open
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

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions frontend/dist/assets/index-CnBnxzjL.css

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion frontend/dist/assets/index-DJuBJMLM.css

This file was deleted.

4 changes: 2 additions & 2 deletions frontend/dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
<meta property="og:title" content="LeLab" />
<meta property="og:description" content="LeRobot but on your browser." />
<meta property="og:type" content="website" />
<script type="module" crossorigin src="/assets/index-BX9JKcmw.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DJuBJMLM.css">
<script type="module" crossorigin src="/assets/index-CUmmU91j.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CnBnxzjL.css">
</head>

<body>
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/components/UrdfViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useUrdf } from "@/hooks/useUrdf";
import { useRealTimeJoints } from "@/hooks/useRealTimeJoints";
import {
createUrdfViewer,
fitGridToRobot,
setupMeshLoader,
setupJointHighlighting,
setupModelLoading,
Expand Down Expand Up @@ -191,6 +192,11 @@ const UrdfViewer: React.FC = () => {
// Get the maximum dimension to ensure the entire robot is visible
const maxDim = Math.max(size.x, size.y, size.z);

// Rescale the ground grid to this robot's actual unit scale — it
// starts as a 1x1 placeholder since it's created before any model
// is loaded (see createUrdfViewer).
fitGridToRobot(viewer, center, boundingBox.min.y, maxDim);

// Position camera to see the center of the model
viewer.camera.position.copy(center);

Expand Down
44 changes: 44 additions & 0 deletions frontend/src/lib/urdfViewerHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Color,
AmbientLight,
DirectionalLight,
GridHelper,
Scene,
} from "three";
import { toast } from "@/components/ui/sonner";
Expand All @@ -32,6 +33,9 @@ export interface URDFViewerElement extends HTMLElement {
scene: Scene;
}

/** Object3D.name of the ground grid added in createUrdfViewer, for lookup by fitGridToRobot. */
export const FLOOR_GRID_NAME = "leLab-floor-grid";

/**
* Creates and configures a URDF viewer element
*/
Expand Down Expand Up @@ -66,12 +70,52 @@ export function createUrdfViewer(
directionalLight.castShadow = true;
viewer.scene.add(directionalLight);

// Ground grid, for a sense of scale and distance while teleoperating.
// Added directly to `scene` (not `world`), which stays in three.js's
// native Y-up frame regardless of the URDF's own up axis — `_setUp` on the
// viewer element compensates for that by rotating `world` instead, so a
// GridHelper here (built flat in the XZ plane) needs no rotation of its
// own to lie under the robot. Sized to a 1x1 unit placeholder at creation;
// `fitGridToRobot` rescales and repositions it once a model's bounding box
// is known, since that can be any real-world unit depending on the URDF.
const grid = new GridHelper(1, 10, 0x6b6b7a, 0x44424f);
grid.name = FLOOR_GRID_NAME;
viewer.scene.add(grid);

// Camera position is no longer adjusted automatically to prevent auto-zooming.
// The user can control the view with the mouse.

return viewer;
}

/**
* Rescale and reposition the ground grid to sit under a newly-loaded robot.
*
* The grid is built at a fixed 1x1 size since createUrdfViewer runs before
* any model exists, so it has no sense of the URDF's unit scale — this call
* (made once the robot's bounding box is known, alongside the existing
* camera auto-fit) is what actually sizes it. `center`/`minY`/`maxDim` are
* passed in rather than recomputed so callers that already have a bounding
* box (fitRobotToView) don't compute it twice.
*/
export function fitGridToRobot(
viewer: URDFViewerElement,
center: Vector3,
minY: number,
maxDim: number
): void {
const grid = viewer.scene.getObjectByName(FLOOR_GRID_NAME);
if (!grid) return;

// Framed a few grid-widths wider than the robot so it reads as a floor
// rather than a tight box drawn around the model.
const size = Math.max(maxDim * 4, 0.01);
grid.scale.setScalar(size);
// A hair below the robot's lowest point avoids z-fighting with anything
// (a gripper, a mounting plate) that sits flush with its own base.
grid.position.set(center.x, minY - size * 0.001, center.z);
}

/**
* Setup mesh loading function for URDF viewer
*/
Expand Down
Loading