xiron 0.5.0

A lightweight 2D robot simulator written in Rust.
Documentation
use macroquad::prelude::*;
use parry2d::math::Vector;
use parry2d::shape::Cuboid;

use crate::behaviour::traits::{Attachable, Drawable};
use crate::parameter::{DT, RESOLUTION};
use crate::utils::{draw_rotated_rectangle, normalise};

#[derive(Debug, Clone)]
pub struct Trolley {
    pub id: String,
    pub pose: (f32, f32, f32),
    pub mount_offset: f32,

    // Shape
    pub shape: Cuboid,
}

impl Trolley {
    pub fn new(id: String, footprint: &(f32, f32)) -> Trolley {
        let shape = Cuboid::new(Vector::new(footprint.0 * 0.5, footprint.1 * 0.5));
        let pose = (0.0, 0.0, 0.0);
        let mount_offset = 0.0;
        Trolley {
            id,
            pose,
            shape,
            mount_offset,
        }
    }

    pub fn from_mount(
        &mut self,
        parent_pos: &(f32, f32, f32),
        mount_offset: f32,
    ) -> Option<Trolley> {
        self.mount(parent_pos, mount_offset);

        return Some(self.clone());
    }
}

impl Attachable for Trolley {
    fn mount(&mut self, parent_pos: &(f32, f32, f32), mount_offset: f32) {
        // Store the mount offset
        self.mount_offset = mount_offset;

        // This will initialise the position of the trolley wrt to the parent.
        let px = parent_pos.0;
        let py = parent_pos.1;
        let ptheta = parent_pos.2;

        let length = self.shape.half_extents[0];
        let width = self.shape.half_extents[1];

        let x = px - (mount_offset + length) * ptheta.cos();
        let y = py - (mount_offset + width) * ptheta.sin();

        self.pose = (x, y, ptheta);
    }

    fn step(&mut self, _mount_pos: &(f32, f32, f32), mount_vel: &(f32, f32, f32)) {
        let mount_v = mount_vel.0;
        let mount_theta = _mount_pos.2;

        let l = self.shape.half_extents[0] * 2.0;

        // The rear axle of the trolley is 3/4 of total length
        let d = self.mount_offset + 3.0 * l / 4.0;

        let v_axle = mount_v * (mount_theta - self.pose.2).cos();
        let w_axle = mount_v * (mount_theta - self.pose.2).sin() / d;

        // Compute the axle world velocities
        let x_dot = v_axle * self.pose.2.cos();
        let y_dot = v_axle * self.pose.2.sin();

        // Compute the velocity of the center
        let a = l / 4.0;
        let xc_dot = x_dot - a * self.pose.2.sin() * w_axle;
        let yc_dot = y_dot + a * self.pose.2.cos() * w_axle;

        // Update the Positions of the trolley
        self.pose.2 = normalise(self.pose.2 + w_axle * DT);
        self.pose.0 = self.pose.0 + xc_dot * DT;
        self.pose.1 = self.pose.1 + yc_dot * DT;
    }
}

impl Drawable for Trolley {
    fn draw(&self, tf: fn((f32, f32)) -> (f32, f32)) {
        // Compute the velocity give the velocity of the parent.
        let extents = self.shape.half_extents;
        let w = extents[0];
        let h = extents[1];

        let rounded_radius = 0.25 * w;

        let w_new = w - rounded_radius;
        let h_new = h - rounded_radius;

        // draw a round rectangle instead of a sharp
        draw_rotated_rectangle(
            (self.pose.0, self.pose.1),
            (w_new, h),
            self.pose.2,
            SKYBLUE,
            tf,
        );
        draw_rotated_rectangle(
            (self.pose.0, self.pose.1),
            (w, h_new),
            self.pose.2,
            SKYBLUE,
            tf,
        );

        let p1 = (w_new, h_new);
        let p2 = (w_new, -h_new);
        let p3 = (-w_new, -h_new);
        let p4 = (-w_new, h_new);

        let params = vec![p1, p2, p3, p4];
        let c = self.pose.2.cos();
        let s = self.pose.2.sin();

        for param in params {
            let (w, h) = param;
            let cx = self.pose.0 + w * c - h * s;
            let cy = self.pose.1 + w * s + h * c;
            let tf_c = tf((cx, cy));
            draw_circle(tf_c.0, tf_c.1, rounded_radius / RESOLUTION, SKYBLUE);
        }
    }

    fn draw_bounds(&self, _tf: fn((f32, f32)) -> (f32, f32)) {}
}