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}