Gizmos API

Interactive manipulation tools for robot joints, providing visual controls for position and rotation adjustments.

JointPositionGizmo

A specialized gizmo for manipulating joint positions along their motion axis.

Constructor

const positionGizmo = new JointPositionGizmo(joint, color, utilityLayer);

Parameters: - joint: Joint - The joint to manipulate - color: BABYLON.Color3 - Gizmo color - utilityLayer: BABYLON.UtilityLayerRenderer - Rendering layer

Properties

Property Type Description
dragBehavior BABYLON.PointerDragBehavior Handles drag interactions
snapDistance number Distance snapping increment (default: 0)
onSnapObservable BABYLON.Observable Event fired when snapping occurs
associatedJoint Joint \| undefined The joint being manipulated
coloredMaterial BABYLON.StandardMaterial Default appearance material
hoverMaterial BABYLON.StandardMaterial Hover state material
disableMaterial BABYLON.StandardMaterial Disabled state material

Usage Example

import { JointPositionGizmo } from '@ranchhandrobotics/babylon_ros';

// Create position gizmo for a prismatic joint
const linearJoint = robot.joints.get("linear_actuator");
if (linearJoint && linearJoint.type === JointType.Prismatic) {
    const positionGizmo = new JointPositionGizmo(
        linearJoint,
        BABYLON.Color3.Blue(), // Blue color for Z-axis
        utilityLayer
    );

    // Configure snapping
    positionGizmo.snapDistance = 0.01; // 1cm increments

    // Listen for position changes
    positionGizmo.dragBehavior.onDragObservable.add(() => {
        console.log(`Joint position: ${linearJoint.transform?.position}`);
    });

    // Attach to joint
    positionGizmo.attachedNode = linearJoint.transform;
}

JointRotationGizmo

A specialized gizmo for manipulating joint rotations around their axis.

Constructor

const rotationGizmo = new JointRotationGizmo(joint, color, utilityLayer);

Parameters: - joint: Joint - The joint to manipulate
- color: BABYLON.Color3 - Gizmo color - utilityLayer: BABYLON.UtilityLayerRenderer - Rendering layer

Properties

Property Type Description
dragBehavior BABYLON.PointerDragBehavior Handles drag interactions
snapDistance number Angular snapping increment in radians (default: 0)
onSnapObservable BABYLON.Observable Event fired when snapping occurs
angle number Accumulated rotation angle (reset on drag start)
sensitivity number Drag sensitivity multiplier (default: 1)
enableLimits boolean Whether to enforce joint limits (default: true)
associatedJoint Joint \| undefined The joint being manipulated
MaxDragAngle number (static) Maximum camera angle for interaction

Usage Example

import { JointRotationGizmo } from '@ranchhandrobotics/babylon_ros';

// Create rotation gizmo for a revolute joint
const shoulderJoint = robot.joints.get("shoulder_joint");
if (shoulderJoint && shoulderJoint.type === JointType.Revolute) {
    const rotationGizmo = new JointRotationGizmo(
        shoulderJoint,
        BABYLON.Color3.Red(), // Red color for X-axis rotation
        utilityLayer
    );

    // Configure snapping to 15-degree increments
    rotationGizmo.snapDistance = Math.PI / 12; // 15 degrees

    // Enable joint limits
    rotationGizmo.enableLimits = true;

    // Adjust sensitivity for finer control
    rotationGizmo.sensitivity = 0.5;

    // Listen for rotation changes
    rotationGizmo.dragBehavior.onDragObservable.add(() => {
        console.log(`Joint angle: ${rotationGizmo.angle} radians`);
    });

    // Attach to joint
    rotationGizmo.attachedNode = shoulderJoint.transform;
}

Gizmo Color Conventions

Standard color coding for different axes and joint types:

// Axis-based colors (following RGB = XYZ convention)
const xAxisColor = BABYLON.Color3.Red();    // X-axis: Red
const yAxisColor = BABYLON.Color3.Green();  // Y-axis: Green  
const zAxisColor = BABYLON.Color3.Blue();   // Z-axis: Blue

// Joint type colors
const revoluteColor = BABYLON.Color3.Yellow();   // Revolute joints
const prismaticColor = BABYLON.Color3.Cyan();    // Prismatic joints
const continuousColor = BABYLON.Color3.Magenta(); // Continuous joints

Automatic Gizmo Creation

The RobotScene class provides automatic gizmo creation based on joint properties:

// Automatic gizmo creation in RobotScene
addExerciseGizmoToJoint(joint: Joint, scene: BABYLON.Scene, layer: BABYLON.UtilityLayerRenderer) {
    if (joint.type === JointType.Fixed) {
        return; // No gizmo for fixed joints
    }

    switch (joint.type) {
        case JointType.Revolute:
        case JointType.Continuous:
            // Create rotation gizmo based on axis
            if (Math.abs(joint.axis.y) > 0.5) {
                // Y-axis rotation - use green gizmo
                this.planeRotationGizmo = new JointRotationGizmo(
                    joint, BABYLON.Color3.Green(), layer
                );
            } else if (Math.abs(joint.axis.z) > 0.5) {
                // Z-axis rotation - use blue gizmo
                this.planeRotationGizmo = new JointRotationGizmo(
                    joint, BABYLON.Color3.Blue(), layer
                );
            } else {
                // X-axis rotation - use red gizmo
                this.planeRotationGizmo = new JointRotationGizmo(
                    joint, BABYLON.Color3.Red(), layer
                );
            }
            break;

        case JointType.Prismatic:
            // Create position gizmo based on axis
            this.planePositionGizmo = new JointPositionGizmo(
                joint, BABYLON.Color3.Cyan(), layer
            );
            break;
    }
}

Gizmo Interaction

Drag Events

Both gizmo types provide drag event observables:

// Position gizmo drag handling
positionGizmo.dragBehavior.onDragStartObservable.add(() => {
    console.log("Started dragging position");
});

positionGizmo.dragBehavior.onDragObservable.add(() => {
    // Update joint position
    if (joint.transform) {
        const position = joint.transform.position;
        console.log(`Position: ${position.x}, ${position.y}, ${position.z}`);
    }
});

positionGizmo.dragBehavior.onDragEndObservable.add(() => {
    console.log("Finished dragging position");
});

// Rotation gizmo drag handling  
rotationGizmo.dragBehavior.onDragObservable.add(() => {
    console.log(`Rotation angle: ${rotationGizmo.angle} radians`);
    console.log(`Rotation degrees: ${rotationGizmo.angle * 180 / Math.PI}°`);
});

Snapping Events

Handle snapping for precise control:

// Position snapping
positionGizmo.snapDistance = 0.005; // 5mm increments
positionGizmo.onSnapObservable.add((snapInfo) => {
    console.log(`Snapped by ${snapInfo.snapDistance} units`);
});

// Rotation snapping  
rotationGizmo.snapDistance = Math.PI / 36; // 5-degree increments
rotationGizmo.onSnapObservable.add((snapInfo) => {
    const degrees = snapInfo.snapDistance * 180 / Math.PI;
    console.log(`Snapped by ${degrees} degrees`);
});

Joint Limits Enforcement

Rotation Limits

const rotationGizmo = new JointRotationGizmo(joint, color, layer);

// Enable limit checking
rotationGizmo.enableLimits = true;

// Limits are automatically read from joint properties
console.log(`Joint limits: ${joint.lowerLimit} to ${joint.upperLimit} radians`);

// The gizmo will prevent movement beyond these limits
rotationGizmo.dragBehavior.onDragObservable.add(() => {
    if (rotationGizmo.enableLimits) {
        // Clamp angle to joint limits
        const clampedAngle = Math.max(joint.lowerLimit, 
                           Math.min(joint.upperLimit, rotationGizmo.angle));

        if (clampedAngle !== rotationGizmo.angle) {
            console.log("Hit joint limit!");
        }
    }
});

Position Limits

const positionGizmo = new JointPositionGizmo(joint, color, layer);

// For prismatic joints, limits apply to translation distance
positionGizmo.dragBehavior.onDragObservable.add(() => {
    if (joint.transform) {
        const distance = joint.transform.position.length();

        if (distance < joint.lowerLimit || distance > joint.upperLimit) {
            console.log("Position outside joint limits!");
        }
    }
});

Material States

Gizmos provide different visual states:

// Access different material states
const gizmo = new JointRotationGizmo(joint, color, layer);

// Default state - colored material
console.log(gizmo.coloredMaterial.name);

// Hover state - brighter/highlighted material
gizmo._gizmoMesh.actionManager = new BABYLON.ActionManager(scene);
gizmo._gizmoMesh.actionManager.registerAction(
    new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPointerOverTrigger, () => {
        gizmo._gizmoMesh.material = gizmo.hoverMaterial;
    })
);

// Disabled state - grayed out material
if (joint.type === JointType.Fixed) {
    gizmo._gizmoMesh.material = gizmo.disableMaterial;
}

Performance Considerations

Utility Layer Management

// Use shared utility layer for multiple gizmos
const utilityLayer = new BABYLON.UtilityLayerRenderer(scene);

// Create multiple gizmos on same layer
const gizmo1 = new JointRotationGizmo(joint1, color, utilityLayer);
const gizmo2 = new JointPositionGizmo(joint2, color, utilityLayer);

// Proper cleanup
gizmo1.dispose();
gizmo2.dispose();
utilityLayer.dispose();

Selective Gizmo Activation

// Only show gizmos for selected joints
function showGizmosForJoint(selectedJoint: Joint) {
    // Clear existing gizmos
    clearAllGizmos();

    // Create gizmo only for selected joint
    if (selectedJoint.type !== JointType.Fixed) {
        createGizmoForJoint(selectedJoint);
    }
}

Integration with RobotScene

Gizmos are typically managed by the RobotScene class:

// Toggle joint exercise mode
robotScene.clearJointExerciseGizmos(); // Clear existing
robotScene.addExerciseGizmoToJoint(selectedJoint, scene, utilityLayer);

// Gizmos automatically:
// - Choose appropriate type based on joint
// - Use proper colors for axes
// - Enforce joint limits
// - Handle coordinate system conversions