blanchon's picture
Update
3cdf7b9
<!-- From https://github.com/brean/urdf-viewer -->
<script lang="ts">
import type IUrdfJoint from "../interfaces/IUrdfJoint";
import { T } from "@threlte/core";
import UrdfLink from "./UrdfLink.svelte";
import { Vector3 } from "three";
import { Billboard, MeshLineGeometry, Text } from "@threlte/extras";
import type IUrdfRobot from "../interfaces/IUrdfRobot";
import { radiansToDegrees } from "@/utils";
type Props = {
robot: IUrdfRobot;
joint: IUrdfJoint;
showName?: boolean;
nameHeight?: number;
jointColor?: string;
jointIndicatorColor?: string;
showLine?: boolean;
opacity?: number;
isInteractive?: boolean;
showVisual?: boolean;
showCollision?: boolean;
visualOpacity?: number;
collisionOpacity?: number;
collisionColor?: string;
jointNames?: boolean;
joints?: boolean;
};
let {
robot,
joint,
showLine = false,
nameHeight = 0.02,
jointColor = "#000000",
jointIndicatorColor = "#000000",
opacity = 0.7,
showName = false,
showVisual = true,
showCollision = true,
visualOpacity = 1,
collisionOpacity = 1,
collisionColor = "#000000",
jointNames = true,
...restProps
}: Props = $props();
// Helper function to get current joint rotation value in degrees
function getJointRotationValue(joint: IUrdfJoint): number {
const axis = joint.axis_xyz || [0, 0, 1];
const rotation = joint.rotation || [0, 0, 0];
// Find the primary axis and get the rotation value
for (let i = 0; i < 3; i++) {
if (Math.abs(axis[i]) > 0.001) {
return radiansToDegrees(rotation[i] / axis[i]);
}
}
return 0;
}
// const ANGLE_RANGES = {
// Elbow: [-196.6593, 1.2308],
// Jaw: [-2.1978, 134.6813],
// Pitch: [-1.0549, 211.8681],
// Wrist_Pitch: [-137.8462, 72.0879],
// Wrist_Roll: [-173.6264, 185.9341],
// Rotation: [-80.3516, 70.6813],
// };
// // Look with the so-arm100.urdf lower limit and upper limit
// // I think 1 in safety_controller = 66 degrees
// function normalizeAngle(value: number, name: keyof typeof ANGLE_RANGES): number {
// if (!(name in ANGLE_RANGES)) {
// throw new Error(`Unknown angle name: ${name}`);
// }
// const [min, max] = ANGLE_RANGES[name];
// const norm = (value - min) / (max - min); // β†’ [0,1]
// return norm * 200 - 100; // β†’ [-100, 100]
// }
// function denormalizeAngle(normValue: number, name: keyof typeof ANGLE_RANGES): number {
// if (!(name in ANGLE_RANGES)) {
// throw new Error(`Unknown angle name: ${name}`);
// }
// const [min, max] = ANGLE_RANGES[name];
// const norm = (normValue + 100) / 200; // β†’ [0,1]
// return norm * (max - min) + min;
// }
// function normalizeAngle2(value: number): number {
// const limit_lower = joint.limit?.lower || 0;
// const limit_upper = joint.limit?.upper || 0;
// const min = limit_lower*66;
// const max = limit_upper*66;
// const norm = (value - min) / (max - min); // β†’ [0,1]
// return norm * 200 - 100; // β†’ [-100, 100]
// }
// function denormalizeAngle2(normValue: number): number {
// const limit_lower = joint.limit?.lower || 0;
// const limit_upper = joint.limit?.upper || 0;
// const min = limit_lower*66;
// const max = limit_upper*66;
// const norm = (normValue + 100) / 200; // β†’ [0,1]
// return norm * (max - min) + min;
// }
</script>
{@html `<!-- Joint ${joint.name} (${joint.type}) -->`}
<!-- draw line from parent-frame to joint origin -->
{#if showLine}
<T.Line>
<MeshLineGeometry
points={[
new Vector3(0, 0, 0),
new Vector3(joint.origin_xyz[0], joint.origin_xyz[1], joint.origin_xyz[2])
]}
/>
<T.LineBasicMaterial color={jointColor} />
</T.Line>
{/if}
<T.Group position={joint.origin_xyz} rotation={joint.origin_rpy}>
<T.Group rotation={joint.rotation}>
{#if joint.child}
<UrdfLink
{robot}
link={joint.child}
textScale={0.2}
{showName}
{showVisual}
{showCollision}
{visualOpacity}
{collisionOpacity}
{collisionColor}
jointNames={true}
joints={true}
{jointColor}
{jointIndicatorColor}
{nameHeight}
{showLine}
opacity={1}
isInteractive={true}
/>
{/if}
{#if showLine}
<T.Line>
<MeshLineGeometry points={[new Vector3(0, 0, 0), new Vector3(0, -0.02, 0)]} />
<T.LineBasicMaterial color={jointIndicatorColor} />
</T.Line>
<T.Mesh rotation={[Math.PI / 2, 0, 0]} {...restProps}>
<T.CylinderGeometry args={[0.004, 0.004, 0.03]} />
<T.MeshBasicMaterial
color={jointColor}
{opacity}
transparent={opacity < 1.0}
/>
</T.Mesh>
{/if}
</T.Group>
</T.Group>
{#if showName}
<Billboard
position.x={joint.origin_xyz[0] + 0.04}
position.y={joint.origin_xyz[1] + 0.01}
position.z={joint.origin_xyz[2] + 0.03}
renderOrder={999}
frustumCulled={false}
>
<!-- text={joint.name + " " + getJointRotationValue(joint).toFixed(0) + "Β°" + " (" + normalizeAngle2(getJointRotationValue(joint)).toFixed(0) + ")"} -->
<Text
scale={nameHeight}
color={jointColor}
text={joint.name + " " + getJointRotationValue(joint).toFixed(0) + "Β°"}
characters="ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890Β°"
renderOrder={999}
></Text>
</Billboard>
{/if}