haply 1.3.1

Haply Robotics Client Library for the Inverse Service
Documentation
//! Haptic interaction with virtual objects (sphere + cube).
//!
//! Usage:
//!   cargo run --example haptic_sample
//!
//! Requires Haply Inverse Service (v3.5+) with an Inverse3 device connected.

use haply::{ compute_total_force, device_model::*, Cube, HaplyDevice, Sphere };
use std::io::Write;

const USE_HTTP: bool = false;
const HTTP_BASE: &str = "http://localhost:10001";
const WS_URL: &str = "ws://localhost:10001/";

#[tokio::main]
async fn main() {
    let mut device = HaplyDevice::new(HTTP_BASE, WS_URL).await.unwrap();

    let configs = device.list_devices().await.unwrap_or_default();
    if configs.is_empty() {
        println!("No devices found. Exiting.");
        return;
    }

    let first_id = configs[0].id().to_string();
    println!("Using device: {}", first_id);

    if !USE_HTTP {
        let _ = device.configure_session(
            SessionConfigure {
                profile: Some(ProfileConfig {
                    name: "haptic_sample".to_string(),
                    required_version: Some(">=3.5".to_string()),
                }),
                basis: None,
                serialization: None,
                sdf: None,
            },
            Some(true)
        ).await;
    }

    let sphere = Sphere {
        center: [0.0, -0.1, 0.18],
        radius: 0.05,
        stiffness: 500.0,
        magnetism: 0.0,
        damping: 0.1,
    };

    // Initial zero-force to engage
    let _ = device.update_force(
        vec![ForceInput { device_id: first_id.clone(), forces: Force { x: 0.0, y: 0.0, z: 0.0 } }],
        Some(false),
        Some(true)
    ).await;
    tokio::time::sleep(std::time::Duration::from_secs(1)).await;

    let objects: Vec<Box<dyn haply::physics_model::PhysicalObject>> = vec![Box::new(sphere)];

    println!("Running haptic loop (Ctrl+C to stop)...");
    loop {
        let state = device.read_state().await.unwrap();

        let pos = state.inverse3
            .first()
            .and_then(|d| d.state.cursor_position)
            .unwrap_or_default();

        let total_force = compute_total_force(pos.into(), &objects);

        print!(
            "\rPos: ({:.4},{:.4},{:.4}) Force: ({:.4},{:.4},{:.4})   ",
            pos.x,
            pos.y,
            pos.z,
            total_force[0],
            total_force[1],
            total_force[2]
        );
        std::io::stdout().flush().unwrap();

        let force = Force { x: total_force[0], y: total_force[1], z: total_force[2] };
        let id = state.inverse3
            .first()
            .map(|d| d.device_id.clone())
            .unwrap_or(first_id.clone());
        let _ = device.update_force(
            vec![ForceInput { device_id: id, forces: force }],
            Some(true),
            Some(true)
        ).await;

        tokio::time::sleep(std::time::Duration::from_millis(1)).await;
    }
}