roast2d_internal 0.3.0-alpha.1

Roast2D internal crate
Documentation
use std::f32::consts::PI;

use glam::{Vec2, Vec3, Vec3Swizzles};

use crate::types::Rect;

impl From<Vec3> for Transform {
    fn from(pos: Vec3) -> Self {
        Self::new(pos, Vec2::ONE)
    }
}

#[derive(Debug, Default, Clone, Copy)]
pub struct Transform {
    pub pos: Vec3,
    pub scale: Vec2,
    /// Angle in radians
    pub angle: f32,
    pub size: Vec2,
}

impl Transform {
    pub fn new(pos: Vec3, size: Vec2) -> Self {
        Self {
            pos,
            size,
            scale: Vec2::ONE,
            angle: 0.0,
        }
    }

    pub fn set_xy(&mut self, Vec2 { x, y }: Vec2) {
        self.pos.x = x;
        self.pos.y = y;
    }

    pub fn with_scale(mut self, scale: Vec2) -> Self {
        self.scale = scale;
        self
    }

    pub fn with_angle(mut self, angle: f32) -> Self {
        self.angle = angle;
        self
    }

    pub fn with_size(mut self, size: Vec2) -> Self {
        self.size = size;
        self
    }

    pub fn with_pos(mut self, pos: Vec3) -> Self {
        self.pos = pos;
        self
    }

    pub fn with_xy(mut self, xy: Vec2) -> Self {
        self.set_xy(xy);
        self
    }

    pub fn scaled_size(&self) -> Vec2 {
        self.size * self.scale
    }

    pub fn bounds(&self) -> Rect {
        self.bounds_with_anchor(Vec2::splat(0.5))
    }

    pub fn bounds_with_anchor(&self, anchor: Vec2) -> Rect {
        calc_bounds(self.pos.xy(), self.scaled_size(), anchor, self.angle)
    }
}

fn calc_bounds(pos: Vec2, size: Vec2, anchor: Vec2, angle: f32) -> Rect {
    const HF_PI: f32 = PI * 0.5;

    // left bottom anchor
    let lb = anchor;
    // right top anchor
    let rt = Vec2::ONE - anchor;

    if angle == 0.0 || angle.abs() == PI {
        let min = pos - (size * lb);
        let max = pos + (size * rt);
        Rect { min, max }
    } else if angle.abs() == HF_PI {
        let size = Vec2 {
            x: size.y,
            y: size.x,
        };
        let min = pos - (size * lb);
        let max = pos + (size * rt);
        Rect { min, max }
    } else {
        let rot = Vec2::from_angle(angle);
        // right top
        let p_rt = size * rt;
        // left bottom
        let p_lb = -size * lb;
        // right bottom
        let p_rb = Vec2::new(p_rt.x, p_lb.y);
        // left top
        let p_lt = Vec2::new(p_lb.x, p_rt.y);
        if angle > 0. && angle < HF_PI {
            let max_x = rot.rotate(p_rb).x;
            let min_x = rot.rotate(p_lt).x;
            let max_y = rot.rotate(p_rt).y;
            let min_y = rot.rotate(p_lb).y;
            Rect {
                min: pos + Vec2::new(min_x, min_y),
                max: pos + Vec2::new(max_x, max_y),
            }
        } else if angle > HF_PI && angle < PI {
            let max_x = rot.rotate(p_lb).x;
            let min_x = rot.rotate(p_rt).x;
            let max_y = rot.rotate(p_rb).y;
            let min_y = rot.rotate(p_lt).y;
            Rect {
                min: pos + Vec2::new(min_x, min_y),
                max: pos + Vec2::new(max_x, max_y),
            }
        } else if angle > -PI && angle < -HF_PI {
            let max_x = rot.rotate(p_lt).x;
            let min_x = rot.rotate(p_rb).x;
            let max_y = rot.rotate(p_lb).y;
            let min_y = rot.rotate(p_rt).y;
            Rect {
                min: pos + Vec2::new(min_x, min_y),
                max: pos + Vec2::new(max_x, max_y),
            }
        } else if angle > -HF_PI && angle < 0.0 {
            let max_x = rot.rotate(p_rt).x;
            let min_x = rot.rotate(p_lb).x;
            let max_y = rot.rotate(p_lt).y;
            let min_y = rot.rotate(p_rb).y;
            Rect {
                min: pos + Vec2::new(min_x, min_y),
                max: pos + Vec2::new(max_x, max_y),
            }
        } else {
            panic!("Unnormalized angle {angle}")
        }
    }
}