Skip to main content

symbios_robot/
turtle.rs

1//! Turtle state and operations for robotic interpretation.
2
3use crate::blueprint::{AxisLimit, JointType, JointTypeKind, MaterialId, ModuleId, SensorType};
4use glam::{Quat, Vec3};
5use serde::{Deserialize, Serialize};
6
7/// Configuration for the next joint to be created.
8///
9/// This acts as a "pen style" for physics. When the turtle spawns a new module attached
10/// to an existing one, it uses these settings to create the connection.
11///
12/// `joint_type` already encodes the drive axis (for Hinge/Prismatic/Screw). The
13/// separate `axis` field here is the *staging* axis: it is what subsequent
14/// `SetJointType` ops apply when constructing a new variant, and what
15/// `SetJointLimits` (single-axis form) tags the appended limit with. This
16/// decouples "what direction the turtle is currently configured for" from
17/// "what variant happens to be active right now".
18#[derive(Clone, Debug, Serialize, Deserialize)]
19pub struct ActiveJointConfig {
20    /// The mechanical type and (where applicable) drive axis of the connection.
21    pub joint_type: JointType,
22
23    /// Staging axis — used to populate axis-bearing variants and to tag
24    /// per-axis limits when no axis is given explicitly. Defaults to `+X`.
25    pub axis: Vec3,
26
27    /// Per-axis physical limits accumulated for the next joint. Empty = unlimited.
28    pub limits: Vec<AxisLimit>,
29}
30
31impl Default for ActiveJointConfig {
32    fn default() -> Self {
33        Self {
34            joint_type: JointType::Fixed, // Default to rigid welding
35            axis: Vec3::X,
36            limits: Vec::new(),
37        }
38    }
39}
40
41/// The state of the Robot Builder Turtle.
42///
43/// Tracks position, orientation, and the topological context (which module we are currently extending).
44#[derive(Clone, Debug, Serialize, Deserialize)]
45pub struct RobotTurtleState {
46    /// Current world-space position of the "cursor".
47    pub position: Vec3,
48
49    /// Current world-space orientation.
50    pub rotation: Quat,
51
52    /// The ID of the module (rigid body) the turtle is currently "standing on".
53    /// If this is Some(id), the NEXT spawned module will be jointed to this one.
54    pub current_module_id: Option<ModuleId>,
55
56    /// Configuration for the next joint creation.
57    pub joint_config: ActiveJointConfig,
58
59    /// Current material ID for new modules.
60    pub material_id: MaterialId,
61
62    /// Current default width/radius for shapes (can be modified by `!`).
63    pub width: f32,
64}
65
66impl Default for RobotTurtleState {
67    fn default() -> Self {
68        Self {
69            position: Vec3::ZERO,
70            rotation: Quat::IDENTITY,
71            current_module_id: None,
72            joint_config: ActiveJointConfig::default(),
73            material_id: 0,
74            width: 0.1,
75        }
76    }
77}
78
79impl RobotTurtleState {
80    /// Returns the turtle's local up direction (Y-axis) in world space.
81    pub fn up(&self) -> Vec3 {
82        self.rotation * Vec3::Y
83    }
84
85    /// Returns the turtle's local forward direction (Z-axis) in world space.
86    pub fn forward(&self) -> Vec3 {
87        self.rotation * Vec3::Z
88    }
89
90    /// Returns the turtle's local right direction (X-axis) in world space.
91    pub fn right(&self) -> Vec3 {
92        self.rotation * Vec3::X
93    }
94
95    /// Rotates the turtle around its local X axis by `angle` radians (Pitch).
96    pub fn rotate_local_x(&mut self, angle: f32) {
97        let rot = Quat::from_axis_angle(Vec3::X, angle);
98        self.rotation *= rot;
99    }
100
101    /// Rotates the turtle around its local Y axis by `angle` radians (Roll).
102    pub fn rotate_local_y(&mut self, angle: f32) {
103        let rot = Quat::from_axis_angle(Vec3::Y, angle);
104        self.rotation *= rot;
105    }
106
107    /// Rotates the turtle around its local Z axis by `angle` radians (Yaw).
108    pub fn rotate_local_z(&mut self, angle: f32) {
109        let rot = Quat::from_axis_angle(Vec3::Z, angle);
110        self.rotation *= rot;
111    }
112}
113
114/// Operations that can be performed by the robot turtle.
115#[derive(Clone, Copy, Debug, PartialEq)]
116pub enum RobotOp {
117    // --- Spatial Navigation ---
118    /// Move forward (along the turtle's growth axis) without spawning geometry (`f`).
119    /// Optional param: `(length)`; defaults to [`crate::RobotConfig::default_length`].
120    Move,
121    /// Rotate around the turtle's local Z axis (`+`/`-`). The wrapped `f32`
122    /// is the per-symbol sign multiplier (`+1.0` or `-1.0`); the rotation
123    /// amount comes from the optional `(angle_deg)` param or
124    /// [`crate::RobotConfig::default_angle`].
125    Yaw(f32),
126    /// Rotate around the turtle's local X axis (`&`/`^`). See [`Yaw`](Self::Yaw)
127    /// for the sign / param semantics.
128    Pitch(f32),
129    /// Rotate around the turtle's local Y axis (`\` / `/`). See [`Yaw`](Self::Yaw)
130    /// for the sign / param semantics.
131    Roll(f32),
132    /// Turn 180° about the turtle's local Z axis (`|`). Equivalent to `Yaw(±π)`.
133    TurnAround,
134
135    // --- Geometry Spawning (The Body) ---
136    /// Spawn a Box shape. Params: `(length, width, depth)` mapping to the
137    /// box's `(Y, X, Z)` extents (Y is the turtle's growth axis).
138    /// If params are missing, falls back to
139    /// `(RobotConfig::default_length, turtle.width, turtle.width)`.
140    SpawnBox,
141    /// Spawn a Cylinder shape (height aligned with the turtle's growth axis).
142    /// Params: `(length, radius)`. Defaults: `length = default_length`,
143    /// `radius = turtle.width / 2`.
144    SpawnCylinder,
145    /// Spawn a Sphere shape. Params: `(radius)`. Default: `turtle.width / 2`.
146    SpawnSphere,
147    /// Spawn a Capsule shape (axis aligned with the turtle's growth axis).
148    /// Params: `(length, radius)`. Defaults: `length = default_length`,
149    /// `radius = turtle.width / 2`.
150    SpawnCapsule,
151
152    // --- Configuration (The Physics) ---
153    /// Set the kind of the NEXT joint to be created.
154    ///
155    /// Axis-bearing variants ([`JointType::Hinge`], [`JointType::Prismatic`],
156    /// [`JointType::Screw`]) are populated from the turtle's staging
157    /// [`ActiveJointConfig::axis`] at interpretation time, so the variant
158    /// passed here can be a placeholder (`Vec3::X` is used by the standard
159    /// symbol map).
160    SetJointType(JointTypeKind),
161    /// Set the staging joint axis. Params: `(x, y, z)`.
162    SetJointAxis,
163    /// Set the screw `pitch` (meters per revolution) for the next [`JointType::Screw`].
164    /// Params: `(pitch)`.
165    SetScrewPitch,
166    /// Append one [`AxisLimit`] using the turtle's current staging axis.
167    /// Params: `(min, max, effort, velocity)`.
168    SetJointLimits,
169    /// Append one [`AxisLimit`] with an explicit axis.
170    /// Params: `(ax, ay, az, min, max, effort, velocity)`.
171    AddAxisLimit,
172    /// Clear any accumulated [`AxisLimit`]s from the staging joint config.
173    ClearJointLimits,
174    /// Set the Material ID for visual rendering. Params: `(material_id)`
175    /// (cast to [`crate::MaterialId`]).
176    SetMaterial,
177    /// Set the turtle's current width / radius used as the default lateral
178    /// extent for subsequent shapes. Params: `(width)`; if omitted the value
179    /// is left unchanged.
180    SetWidth,
181
182    // --- Attachments (The Senses) ---
183    /// Mount a sensor at the current location.
184    MountSensor(SensorType),
185    /// Declare an end-effector / TCP frame at the turtle's current pose,
186    /// pinned to the module the turtle is currently standing on.
187    /// Params: `(ee_id)`.
188    MarkEndEffector,
189
190    // --- Flow Control ---
191    /// Save the full turtle state onto the stack (`[`).
192    Push,
193    /// Restore the most recently pushed turtle state (`]`).
194    Pop,
195    /// No-op — symbol has no registered meaning.
196    Ignore,
197}