Spaces:
Running
Running
<!-- 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} | |