haply 1.1.0

Haply Robotics Client Library for the Inverse Service
Documentation
//! Navigation module types — bubble navigation settings, state, status, and configure.

use serde::{Deserialize, Serialize};
use ts_rs::TS;

use super::base_types::Linear3D;
use super::primitives::{CenterMode, EasingType, SdfPrimitive};

// ============================================================
// Incoming state/status (from service → client)
// ============================================================

/// Navigation state contributed to Inverse3 device state when navigation is active.
#[derive(Clone, Debug, PartialEq, Default, Deserialize, Serialize, TS)]
pub struct NavigationState {
    #[serde(default)]
    pub bubble: Option<BubbleNavigationState>,
}

/// Bubble navigation runtime state.
#[derive(Copy, Clone, Debug, PartialEq, Default, Deserialize, Serialize, TS)]
pub struct BubbleNavigationState {
    #[serde(default)]
    pub center: Option<Linear3D>,
    #[serde(default)]
    pub velocity_zone_width: Option<f32>,
    #[serde(default)]
    pub collision_extra_inflate: Option<f32>,
}

/// Navigation status contributed to Inverse3 device status.
#[derive(Clone, Debug, PartialEq, Default, Deserialize, Serialize, TS)]
pub struct NavigationStatusInfo {
    #[serde(default)]
    pub mode: Option<String>,
}

// ============================================================
// Full HTTP GET response (config + state + status combined)
// ============================================================

/// Combined response from `GET /{type}/{id}/config/navigation`.
#[derive(Clone, Debug, PartialEq, Default, Deserialize, Serialize, TS)]
pub struct NavigationFullResponse {
    #[serde(default)]
    pub config: Option<BubbleNavigationSettings>,
    #[serde(default)]
    pub state: Option<BubbleNavigationState>,
    #[serde(default)]
    pub status: Option<NavigationStatusInfo>,
}

// ============================================================
// Outgoing configure (client → service)
// ============================================================

/// Outgoing navigation configure command.
///
/// Set `mode` to `"bubble"` to start/update, or `"disabled"` to stop.
#[derive(Clone, Debug, PartialEq, Default, Deserialize, Serialize, TS)]
pub struct NavigationConfigure {
    pub mode: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bubble: Option<BubbleNavigationSettings>,
}

// ============================================================
// Full bubble navigation settings (~30 fields)
// ============================================================

/// Complete bubble navigation settings.
/// All fields are `Option` — only set fields are serialized/applied; absent fields keep defaults.
#[derive(Clone, Debug, PartialEq, Default, Deserialize, Serialize, TS)]
pub struct BubbleNavigationSettings {
    /// Initial bubble center in mount space.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub center: Option<Linear3D>,

    /// SDF shape defining the dead zone (default: sphere r=0.05).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub shape: Option<SdfPrimitive>,

    // --- Velocity ---

    /// Width of the rate-control shell in meters (default: 0.03).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub velocity_zone_width: Option<f32>,

    /// Maximum navigation velocity in m/s (default: 1.0).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_velocity: Option<f32>,

    /// Easing curve for velocity ramp (default: linear).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub velocity_ease: Option<EasingType>,

    /// Zero entry speed when entering velocity zone (default: true).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reset_velocity_on_entry: Option<bool>,

    // --- Bump ---

    /// Tactile bump width at surface in meters (default: 0.003).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bump_width: Option<f32>,

    /// Bump spring stiffness (default: 500.0).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bump_stiffness: Option<f32>,

    // --- Spring gains ---

    /// Spring gain at bubble center (default: 4.0).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub spring_inner: Option<f32>,

    /// Spring gain at bubble surface d=0 (default: 7.0).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub spring_surface: Option<f32>,

    /// Spring gain at outer boundary d=W (default: 12.0).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub spring_outer: Option<f32>,

    /// Hard wall spring stiffness (default: 700.0).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub wall_stiffness: Option<f32>,

    // --- Damping gains ---

    /// Damping at bubble center (default: 0.2).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub damping_inner: Option<f32>,

    /// Damping at bubble surface (default: 0.7).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub damping_surface: Option<f32>,

    /// Damping at outer boundary (default: 5.0).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub damping_outer: Option<f32>,

    // --- Rotation / Scale ---

    /// Apply workspace rotation to navigation direction (default: false).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub rotation_enabled: Option<bool>,

    /// Apply workspace scale to navigation velocity (default: false).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub scale_enabled: Option<bool>,

    // --- Center tracking ---

    /// How the bubble center tracks the cursor (default: auto_follow).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub center_mode: Option<CenterMode>,

    /// Auto-centering drift speed in m/s for auto_follow mode (default: 0.03).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub center_drift_speed: Option<f32>,

    // --- Workspace bounding ---

    /// Clamp center to device workspace SDF (default: false).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub workspace_bounded: Option<bool>,

    /// Speed of initial workspace clamping transition (default: 1.2).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub workspace_transition_speed: Option<f32>,

    /// Easing for workspace transition (default: quadratic_in_out).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub workspace_transition_ease: Option<EasingType>,

    // --- Collision ---

    /// Block navigation in collision direction (default: false).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub stop_at_collision: Option<bool>,

    /// External force threshold to detect collision in N (default: 0.001).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub collision_threshold: Option<f32>,

    /// Multiplier for zone width during collision (default: 2.0).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub collision_inflate_scale: Option<f32>,

    // --- Avatar boundary ---

    /// Activate avatar boundary clamping (default: false).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub avatar_boundary_enabled: Option<bool>,

    /// SDF shape for avatar position constraint (default: sphere r=0.1).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub avatar_boundary: Option<SdfPrimitive>,
}