spottedcat 1.0.1

Rusty SpottedCat simple game engine
Documentation
use spottedcat::{Context, DrawOption, Image, Pt, Spot, Text, WindowConfig};

const DISC_SIZE: usize = 160;

struct RotationAspectTest {
    disc: Image,
    font_id: u32,
    angle: f32,
}

fn make_disc_rgba() -> Vec<u8> {
    let mut rgba = vec![0u8; DISC_SIZE * DISC_SIZE * 4];
    let center = (DISC_SIZE as f32 - 1.0) * 0.5;
    let radius = DISC_SIZE as f32 * 0.43;
    let ring_inner = radius - 4.0;

    for y in 0..DISC_SIZE {
        for x in 0..DISC_SIZE {
            let fx = x as f32 - center;
            let fy = y as f32 - center;
            let d = (fx * fx + fy * fy).sqrt();
            let idx = (y * DISC_SIZE + x) * 4;

            if d > radius {
                continue;
            }

            let on_ring = d >= ring_inner;
            let on_cross = fx.abs() < 3.0 || fy.abs() < 3.0;
            let on_diagonal = (fx - fy).abs() < 3.0;
            let color = if on_ring {
                [255, 255, 255, 255]
            } else if on_cross {
                [255, 78, 78, 255]
            } else if on_diagonal {
                [70, 220, 255, 255]
            } else {
                [40, 185, 120, 255]
            };

            rgba[idx..idx + 4].copy_from_slice(&color);
        }
    }

    rgba
}

fn top_left_for_center_rotation(center_x: f32, center_y: f32, size: f32, angle: f32) -> [Pt; 2] {
    let half = size * 0.5;
    let c = angle.cos();
    let s = angle.sin();
    let center_offset_x = c * half + s * half;
    let center_offset_y = c * half - s * half;

    [
        Pt::from(center_x - center_offset_x),
        Pt::from(center_y - center_offset_y),
    ]
}

impl Spot for RotationAspectTest {
    fn initialize(ctx: &mut Context) -> Self {
        let disc = Image::new(
            ctx,
            Pt::from(DISC_SIZE as f32),
            Pt::from(DISC_SIZE as f32),
            &make_disc_rgba(),
        )
        .expect("disc image should load");
        let font_id =
            spottedcat::register_font(ctx, include_bytes!("../assets/DejaVuSans.ttf").to_vec());

        Self {
            disc,
            font_id,
            angle: 0.0,
        }
    }

    fn update(&mut self, _ctx: &mut Context, dt: std::time::Duration) {
        self.angle += dt.as_secs_f32();
    }

    fn draw(&mut self, ctx: &mut Context, screen: Image) {
        let (w, h) = spottedcat::window_size(ctx);
        let sw = w.as_f32();
        let sh = h.as_f32();
        let disc = DISC_SIZE as f32;

        let title = Text::new("DrawOption::with_rotation aspect test", self.font_id)
            .with_font_size(Pt::from(20.0))
            .with_color([0.96, 0.98, 1.0, 1.0]);
        screen.draw(
            ctx,
            &title,
            DrawOption::default().with_position([Pt::from(18.0), Pt::from(22.0)]),
        );

        let hint = Text::new(
            "Both discs should stay circular in this portrait window.",
            self.font_id,
        )
        .with_font_size(Pt::from(14.0))
        .with_color([0.78, 0.84, 0.88, 1.0]);
        screen.draw(
            ctx,
            &hint,
            DrawOption::default().with_position([Pt::from(18.0), Pt::from(52.0)]),
        );

        let center_y = (sh * 0.42).max(92.0 + disc * 0.5);
        let left_center_x = sw * 0.5 - disc * 0.5 - 22.0;
        let right_center_x = sw * 0.5 + disc * 0.5 + 22.0;
        let left_pos = top_left_for_center_rotation(left_center_x, center_y, disc, 0.0);
        let right_pos = top_left_for_center_rotation(right_center_x, center_y, disc, self.angle);

        screen.draw(
            ctx,
            &self.disc,
            DrawOption::default().with_position(left_pos),
        );

        screen.draw(
            ctx,
            &self.disc,
            DrawOption::default()
                .with_position(right_pos)
                .with_rotation(self.angle),
        );

        let y = center_y - disc * 0.5;
        let left_x = left_center_x - disc * 0.5;
        let right_x = right_center_x - disc * 0.5;
        let labels_y = y + disc + 18.0;
        let reference = Text::new("0 rad", self.font_id)
            .with_font_size(Pt::from(15.0))
            .with_color([0.92, 0.94, 0.96, 1.0]);
        screen.draw(
            ctx,
            &reference,
            DrawOption::default().with_position([Pt::from(left_x + 48.0), Pt::from(labels_y)]),
        );

        let rotating = Text::new("rotating", self.font_id)
            .with_font_size(Pt::from(15.0))
            .with_color([0.92, 0.94, 0.96, 1.0]);
        screen.draw(
            ctx,
            &rotating,
            DrawOption::default().with_position([Pt::from(right_x + 36.0), Pt::from(labels_y)]),
        );
    }

    fn remove(&mut self, _ctx: &mut Context) {}
}

fn main() {
    spottedcat::run::<RotationAspectTest>(WindowConfig {
        title: "Rotation Aspect Test".to_string(),
        width: Pt::from(390.0),
        height: Pt::from(844.0),
        ..Default::default()
    });
}